# vue 实例初始化的过程
- 从/src/core/index文件开始
引入构建 Vue 的实例方法- 定义了 Vue 的原型方法 $isServer,$ssrContext
- 定义了 Vue 的静态方法 FunctionalRenderContext
function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
initMixin(Vue) // 定义了 Vue 原型方法_init;
stateMixin(Vue) // 定义了 Vue 原型方法 $data, $props, $set, $delete, $watch; 该文件里包含initState, initProps, initData, initComputed, initMethods, initWatch 的定义
eventsMixin(Vue)// 定义了 Vue 原型方法 $on, $once, $off, $emit
lifecycleMixin(Vue) // 定义了 Vue 原型方法 _update, $forceUpdate;该文件里包含 initLifecycle 的定义
renderMixin(Vue) // 定义了 Vue 原型方法 $nextTick, _render;该文件里包含 initRender 的定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
- 初始化完成了哪些工作?
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
vm._uid = uid++
vm._isVue = true
if (options && options._isComponent) {
// 内部组件
initInternalComponent(vm, options)
} else {
// 合并配置
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
// TODO
if (process.env.NODE_ENV !== 'production') {
initProxy(vm)
} else {
vm._renderProxy = vm
}
// expose real self
vm._self = vm
initLifecycle(vm) // 完成 vm 实例的初始化属性「$children, $refs, _watcher, _inactive...」,并把 vm 放入到合适的 $children 节点 或 $root节点
initEvents(vm) // 初始化实例的_event对象,_hasHookEvent属性,更新父组件的事件监听updateListeners
initRender(vm) // 初始化实例的_vnode, $slots, $scopeSlots, _c, $createElement, 追踪了$attrs, $listeners
callHook(vm, 'beforeCreate')
initInjections(vm) // 根据 Inject 的 key,获取到vm实例的_provided 或 inject[key].defalut 所对应的数据,进行追踪----resolve injections before data/props
initState(vm) // 初始化实例属性:_watchers,并执行:initProps, initMethods,有配置data时initData,否则追踪空数据,initComputed,initWatch
initProvide(vm) // 初始化实例属性:_provided----resolve provide after data/props
callHook(vm, 'created')
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
vm._name = formatComponentName(vm, false)
mark(endTag)
measure(`vue ${vm._name} init`, startTag, endTag)
}
// 挂载实例
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
1
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
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
- callHook的实现
export function callHook (vm: Component, hook: string) {
// 将 vm 更新到 targetStack 栈中,并更新 Dep.target
pushTarget()
const handlers = vm.$options[hook]
const info = `${hook} hook`
if (handlers) {
for (let i = 0, j = handlers.length; i < j; i++) {
// 执行生命周期回调函数
invokeWithErrorHandling(handlers[i], vm, null, vm, info)
}
}
if (vm._hasHookEvent) {
vm.$emit('hook:' + hook)
}
// 将 vm 从 targetStack 栈中移除,并更新 Dep.target 为targetStack的栈顶元素
popTarget()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- initState
toggleObserving
该方法提供了控制开始与结束创建追踪的机制
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props) // 追踪vm.$options.propsOptions数据,并更新vm.$options._propKeys
if (opts.methods) initMethods(vm, opts.methods) // 将方法挂到 vm 实例上,并绑定执行上下文对象为vm
if (opts.data) {
initData(vm) // 初始化了 vm._data;先检查了methods, props是否有同名数据,再对 data 进行数据追踪
} else {
observe(vm._data = {}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed) // 初始化了vm._computedWatchers,不是服务端渲染则对其getter创建watcher进行监听,并把 computed 方法绑定到 vm 上,并定义 commputedGetter 方法
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch) // 创建vm.$watch监听
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- 挂载
- 创建组件是有子组件挂载:child.$mount
- 根据不同的平台提供了web、webRuntime、weexRuntime 的挂载方法
- webRuntime版本需要处理模板编译 compileToFunctions 返回render 与 staticRenderFns方法,再调用 Web 的$mount方法
- Vue.compile = compileToFunctions
- 其他两个版本的 $mount 都是调用 mountComponent,其定义了updateComponent方法
// 非生成环境
updateComponent = () => {
const name = vm._name
const id = vm._uid
const startTag = `vue-perf-start:${id}`
const endTag = `vue-perf-end:${id}`
mark(startTag)
const vnode = vm._render()
mark(endTag)
measure(`vue ${name} render`, startTag, endTag)
mark(startTag)
vm._update(vnode, hydrating)
mark(endTag)
measure(`vue ${name} patch`, startTag, endTag)
}
updateComponent = () => {
vm._update(vm._render(), hydrating)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20