# 全局api解析
友情提示:点击每个api的标题可以直接打开API的文档
上篇说到了vue的初始化流程,其中给原型初始化的过程我们之后再分析,先把不执行new就能用的全局Api看了。 我的思路是根据initGlobalApi(Vue)方法的顺序往下看
# Vue.config
这是一个全局属性,默认是读取的这个配置
- silent
- 默认值:
false
- 作用:是否取消 Vue 所有的日志与警告。
- 默认值:
- optionMergeStrategies
- 类型:
{ [key: string]: Function }
- 默认值
{}
- 作用:自定义合并策略的选项
- 类型:
- devtools
- 默认值:开发版本默认为
true
,生产版本默认为false
- 作用:配置是否允许
vue-devtools
检查代码。生产版本设为 true 可以启用检查。
- 默认值:开发版本默认为
- errorHandler
- 类型:
function
- 默认值:
undefined
- 作用:指定组件的渲染和观察期间未捕获错误的处理函数。这个处理函数被调用时,可获取错误信息和 Vue 实例。
- 类型:
- warnHandler
- 类型:
Function
- 默认值:
undefined
- 作用:为 Vue 的运行时警告赋予一个自定义处理函数。注意这只会在开发者环境下生效,在生产环境下它会被忽略。
- 类型:
- ignoredElements
- 类型:
Array<string | RegExp>
- 默认值:
[]
- 作用:须使 Vue 忽略在 Vue 之外的自定义元素 (e.g. 使用了
Web Components APIs
)。否则,它会假设你忘记注册全局组件或者拼错了组件名称,从而抛出一个关于Unknown custom element
的警告。
- 类型:
- keyCodes
- 类型:
{ [key: string]: number | Array<number> }
- 默认值:
{}
- 作用:给
v-on
自定义键位别名。
- 类型:
- performance
- 类型:
boolean
- 默认值:
false
- 作用: 设置为
true
以在浏览器开发工具的性能/时间线面板中启用对组件初始化、编译、渲染和打补丁的性能追踪。只适用于开发模式和支持performance.mark API
的浏览器上
- 类型:
- productionTip
- 类型:
boolean
- 默认值:
process.env.NODE_ENV !== 'production'
- 作用:设置为
false
以阻止vue
在启动时生成生产提示。
- 类型:
配置文件里还有一些属性,不过没有写到文档里,所以应该不推荐用户修改配置,所以就不说啦。
# Vue.set
设置对象的属性。如果对象是响应式的,确保属性被创建后也是响应式的,同时触发视图更新。这个方法主要用于避开Vue
不能检测属性被添加的限制。
// ./global-api/index.js
import { set, del } from '../observer/index'
// ...
Vue.set = set
// ...
// './observer/index.js'
/**
* Set a property on an object. Adds the new property and
* triggers change notification if the property doesn't
* already exist.
*/
export function set(target, key, val) {
// 如果target是一个数组,而且key是一个数组的索引值,直接用数组方法splice()可以直接触发响应的监听,返回
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.length = Math.max(target.length, key)
target.splice(key, 1, val)
return val
}
// 如果key在target里,而且key不是在Object本身就有的属性,直接更新返回
if (key in target && !(key in Object.prototype)) {
target[key] = val
return val
}
// 缓存观察者
const ob = (target).__ob__
// 判断target是否是Vue实例或者根数据对象,如果是,则报错,所以
if (target._isVue || (ob && ob.vmCount)) {
process.env.NODE_ENV !== 'production' && warn('Avoid adding reactive properties to a Vue instance or its root $data ' + 'at runtime - declare it upfront in the data option.')
return val
}
// 如果target不是响应式的,就直接返回修改后的值
if (!ob) {
target[key] = val
return val
}
// 传入defineReactive方法,让target的key属性可响应式,然后触发观察者的依赖的notify()方法
defineReactive(ob.value, key, val)
ob.dep.notify()
return val
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# Vue.delete
// ./global-api/index.js
import { set, del } from '../observer/index'
// ...
Vue.delete = del
// ...
// './observer/index.js'
/**
* Delete a property and trigger change if necessary.
*/
export function del (target: Array<any> | Object, key: any) {
// 如果target是一个数组,而且key是一个数组的索引值,直接用数组方法splice()可以直接触发响应的监听,返回
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.splice(key, 1)
return
}
const ob = (target).__ob__
// 判断target是否是Vue实例或者根数据对象,如果是,则报错
if (target._isVue || (ob && ob.vmCount)) {
process.env.NODE_ENV !== 'production' && warn(
'Avoid deleting properties on a Vue instance or its root $data ' +
'- just set it to null.'
)
return
}
// 如果key不是target的属性,返回
if (!hasOwn(target, key)) {
return
}
delete target[key]
// 如果target不是响应的,返回
if (!ob) {
return
}
// 手动通知
ob.dep.notify()
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# Vue.nextTick
在下次DOM
更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
2.1.0 起新增:如果没有提供回调且在支持
Promise
的环境中,则返回一个Promise
。
代码里的microTimerFunc
和macroTimerFunc
分别是 微任务 和 宏任务 ,这里的microTimerFunc
主要是用的Promise
,若不支持Promise
则microTimerFunc=macroTimerFunc
。macroTimerFunc
则是用的setImmediate
,若不支持setImmediate
则用setTimeout
代码比较简单,就是推入一个callback
数组,然后等待被调用
const callbacks = []
let pending = false
// Here we have async deferring wrappers using both micro and macro tasks.
// In < 2.4 we used micro tasks everywhere, but there are some scenarios where
// micro tasks have too high a priority and fires in between supposedly
// sequential events (e.g. #4521, #6690) or even between bubbling of the same
// event (#6566). However, using macro tasks everywhere also has subtle problems
// when state is changed right before repaint (e.g. #6813, out-in transitions).
// Here we use micro task by default, but expose a way to force macro task when
// needed (e.g. in event handlers attached by v-on).
let microTimerFunc
let macroTimerFunc
let useMacroTask = false
function flushCallbacks () {
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
export function nextTick (cb?: Function, ctx?: Object) {
let _resolve
callbacks.push(() => {
if (cb) {
try {
cb.call(ctx)
} catch (e) {
handleError(e, ctx, 'nextTick')
}
} else if (_resolve) {
_resolve(ctx)
}
})
if (!pending) {
pending = true
if (useMacroTask) {
/**
* macroTimerFunc = () => {
* setImmediate(flushCallbacks)
* }
*/
macroTimerFunc()
} else {
/**
* const p = Promise.resolve()
* microTimerFunc = () => {
* p.then(flushCallbacks)
* }
*/
microTimerFunc()
}
}
if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
_resolve = resolve
})
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# Vue.use
安装
Vue.js
插件。如果插件是一个对象,必须提供install
方法。如果插件是一个函数,它会被作为install
方法。install
方法调用时,会将 Vue 作为参数传入。当install
方法被同一个插件多次调用,插件将只会被安装一次。
export function initUse (Vue: GlobalAPI) {
Vue.use = function (plugin: Function | Object) {
// 缓存或初始化 installedPlugins
const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
// 若已经存在该plugin(引用相同),返回
if (installedPlugins.indexOf(plugin) > -1) {
return this
}
// additional parameters
const args = toArray(arguments, 1) // Convert an Array-like object to a real Array
args.unshift(this)
if (typeof plugin.install === 'function') {
plugin.install.apply(plugin, args)
} else if (typeof plugin === 'function') {
plugin.apply(null, args)
}
installedPlugins.push(plugin) // this._installedPlugins同时会改变
return this
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Vue.mixin
全局注册一个混入,影响注册之后所有创建的每个 Vue
实例。插件作者可以使用混入,向组件注入自定义的行为。不推荐在应用代码中使用。
export function initMixin (Vue: GlobalAPI) {
Vue.mixin = function (mixin: Object) {
this.options = mergeOptions(this.options, mixin)
return this
}
}
2
3
4
5
6
# Vue.extend
使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。
data
选项是特例,需要注意 - 在 Vue.extend()
中它必须是函数。这里我的理解是因为创建出来的子类的实例会共享这个options.data配置,实例之间对于data的修改也会互相影响
export function initExtend (Vue: GlobalAPI) {
/**
* Each instance constructor, including Vue, has a unique
* cid. This enables us to create wrapped "child
* constructors" for prototypal inheritance and cache them.
*/
Vue.cid = 0
let cid = 1
/**
* Class inheritance
*/
Vue.extend = function (extendOptions: Object): Function {
extendOptions = extendOptions || {}
const Super = this
const SuperId = Super.cid
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
if (cachedCtors[SuperId]) { // 如果有通过该构造器和options对象构造的示例缓存,直接返回
return cachedCtors[SuperId]
}
const name = extendOptions.name || Super.options.name // 读取name或Super的name
if (process.env.NODE_ENV !== 'production' && name) {
// 校验name属性值
validateComponentName(name)
}
const Sub = function VueComponent (options) { // Sub类,调用Vue的构造函数_init
this._init(options)
}
// 组合继承
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
Sub.cid = cid++
// 合并options
Sub.options = mergeOptions(
Super.options,
extendOptions
)
// 在./instance/init.js resolveConstructorOptions等方法中会用到这个字段
Sub['super'] = Super
// For props and computed properties, we define the proxy getters on
// the Vue instances at extension time, on the extended prototype. This
// avoids Object.defineProperty calls for each instance created.
// 把props和computed放在原型链上,这样可以避免每次实例创建都要去调用Object.defineProperty
if (Sub.options.props) {
initProps(Sub)
}
if (Sub.options.computed) {
initComputed(Sub)
}
// allow further extension/mixin/plugin usage
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use
// create asset registers, so extended classes
// can have their private assets too.
ASSET_TYPES.forEach(function (type) {
Sub[type] = Super[type]
})
// enable recursive self-lookup
if (name) {
Sub.options.components[name] = Sub
}
// 保存一个Super.options的引用,在之后实例化的时候我们可以判断是否父类的options发生了更新
Sub.superOptions = Super.options
Sub.extendOptions = extendOptions
Sub.sealedOptions = extend({}, Sub.options)
// cache constructor 缓存
cachedCtors[SuperId] = Sub
return Sub
}
function initProps (Comp) {
const props = Comp.options.props
for (const key in props) {
proxy(Comp.prototype, `_props`, key)
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# Vue.components、Vue.directive、Vue.filter
Vue.components、Vue.directive、Vue.filter
三个方法的定义都写在了initAssetRegisters
方法里。
- Vue.components:注册或获取全局组件。注册还会自动使用给定的
id
设置组件的名称 参考:组件 - Vue.directive: 注册或获取全局指令。 参考:自定义指令
- Vue.filter: 注册或获取全局过滤器。参考:过滤器
它们的用法类似:
// 注册组件,传入一个扩展过的构造器
Vue.component('my-component', Vue.extend({ /* ... */ }))
// 注册组件,传入一个选项对象 (自动调用 Vue.extend)
Vue.component('my-component', { /* ... */ })
// 获取注册的组件 (始终返回构造器)
var MyComponent = Vue.component('my-component')
// directive
// 注册
// 钩子函数
Vue.directive('my-directive', {
bind: function () {}, // 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
inserted: function () {}, // 被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
update: function () {}, // 所在组件的 VNode 更新时调用
componentUpdated: function () {}, // 指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind: function () {} // 只调用一次,指令与元素解绑时调用。
})
// 注册 (指令函数)
Vue.directive('my-directive', function () {
// 这里将会被 `bind` 和 `update` 调用
})
// getter,返回已注册的指令
var myDirective = Vue.directive('my-directive')
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
定义:
// export const ASSET_TYPES = [
// 'component',
// 'directive',
// 'filter'
// ]
export function initAssetRegisters (Vue: GlobalAPI) {
/**
* Create asset registration methods.
*/
ASSET_TYPES.forEach(type => {
Vue[type] = function (
id: string,
definition: Function | Object
): Function | Object | void {
// 如果没有传入definition, 则通过id去返回之前注册过的
if (!definition) {
return this.options[type + 's'][id]
} else {
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && type === 'component') {
// 校验components的名字
validateComponentName(id)
}
if (type === 'component' && isPlainObject(definition)) {
// 如果是component, 且传入的是个对象,则用component被初始化前的状态去调用extend
definition.name = definition.name || id
definition = this.options._base.extend(definition)
}
if (type === 'directive' && typeof definition === 'function') {
// 如果是directive,且传入的是个函数,则给bind和update赋值
definition = { bind: definition, update: definition }
}
// 注册
this.options[type + 's'][id] = definition
return definition
}
}
})
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39