vue源码阅读(二) 核心函数

第三步: vm的生命周期相关变量初始化

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
export function initLifecycle (vm: Component) {
const options = vm.$options

// 这里判断是否存在父示例,如果存在,则通过 while 循环,建立所有组建的父子关系
let parent = options.parent
if (parent && !options.abstract) {
while (parent.$options.abstract && parent.$parent) {
parent = parent.$parent
}
parent.$children.push(vm)
}
// 为组件实例挂载相应属性,并初始化
vm.$parent = parent
vm.$root = parent ? parent.$root : vm

vm.$children = []
vm.$refs = {}

vm._watcher = null
vm._inactive = null
vm._directInactive = false
vm._isMounted = false
vm._isDestroyed = false
vm._isBeingDestroyed = false
}

第四步: vm的事件监听初始化

1
2
3
4
5
6
7
8
9
10
11
12
export function initEvents (vm: Component) {
vm._events = Object.create(null)
// 标记是否有钩子函数
vm._hasHookEvent = false
// init parent attached events
// _parentListeners其实是父组件模板中写的v-on
// 所以下面这段就是将父组件模板中注册的事件放到当前组件实例的listeners里面
const listeners = vm.$options._parentListeners
if (listeners) {
updateComponentListeners(vm, listeners)
}
}

初始化呈现

​ initRender(vm)

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
export function initRender (vm: Component) {
vm._vnode = null
vm._staticTrees = null
//$options 用于当前 Vue 实例的初始化选项。需要在选项中包含自定义属性时会有用处
const options = vm.$options
const parentVnode = vm.$vnode = options._parentVnode 父树中的占位符节点
const renderContext = parentVnode && parentVnode.context
//$slots 用来访问被插槽分发的内容
vm.$slots = resolveSlots(options._renderChildren, renderContext)
vm.$scopedSlots = emptyObject
// 将createElement fn绑定到此实例,以便在其中获得适当的渲染上下文。
// 参数顺序:标记、数据、子项、规范化类型、AlwaysSnormalize
// 内部版本由从模板编译的呈现函数使用
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
// 规范化始终应用于公共版本,在用户编写的呈现函数中使用。
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)

const parentData = parentVnode && parentVnode.data
// $attrs和$listeners是为了更容易地创建hoc而公开的。它们需要具有响应性,以便使用它们的hoc总是得到更新。
if (process.env.NODE_ENV !== 'production') {
defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, () => {
!isUpdatingChildComponent && warn(`$attrs is readonly.`, vm)
}, true)
defineReactive(vm, '$listeners', options._parentListeners || emptyObject, () => {
!isUpdatingChildComponent && warn(`$listeners is readonly.`, vm)
}, true)
} else {
defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true)
defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null, true)
}
/* istanbul ignore else */
}

调用生命周期钩子

1
callHook(vm, 'beforeCreate')

第五步: vm的状态初始化,prop/data/computed/method/watch都在这里完成初始化,因此也是Vue实例create的关键。

初始注入

提示:provideinject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。 然后我们来看一下initInjectionsinitProvide的执行顺序:

先初始化initInjections

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
export function initInjections (vm: Component) {
// 因为并没有`vm._provided此时 `result 返回的是个 null,也就没有进行`defineReactive`
const result = resolveInject(vm.$options.inject, vm)
if (result) {
toggleObserving(false)
Object.keys(result).forEach(key => {
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
defineReactive(vm, key, result[key], () => {
warn(
`Avoid mutating an injected value directly since the changes will be ` +
`overwritten whenever the provided component re-renders. ` +
`injection being mutated: "${key}"`,
vm
)
})
} else {
defineReactive(vm, key, result[key])
}
})
toggleObserving(true)
}
}

然后运行initState

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props)
if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}

接着定义我们的initProvide

1
2
3
4
5
6
7
8
export function initProvide (vm: Component) {
const provide = vm.$options.provide
if (provide) {
vm._provided = typeof provide === 'function'
? provide.call(vm)
: provide
}
}