携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第27天,点击查看活动详情
我们继续来看Vue实例初始化的时候_init方法做了什么:
// 源码文件:src\core\instance\init.ts
//...
vm._self = vm
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate', undefined, false /* setContext */)
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
//...
这是一系列的初始化和生命周期钩子的调用,今天我们拿两个出来说,生命周期的初始化和生命周期钩子的调用:
initLifecycle(vm)
callHook(vm, 'beforeCreate', undefined, false /* setContext */)
callHook(vm, 'created')
生命周期的初始化
与其说是生命周期的初始化,initLifecycle不如说是父组件的绑定:
export function initLifecycle(vm: Component) {
const options = vm.$options
// 定位到第一个非抽象组件
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._provided = parent ? parent._provided : Object.create(null)
vm._watcher = null
vm._inactive = null
vm._directInactive = false
vm._isMounted = false
vm._isDestroyed = false
vm._isBeingDestroyed = false
}
当option中的parent存在,且当前组件不是抽象组件时,向上逐层查找,直到查找到第一个非抽象组件的父组件,将组件加入到该父组件的$children中,同时,将该组件的$parent指向这个父组件。
接着给实例挂载$root属性,逻辑是如果上述父组件存在,则指向那个父组件,如果不存在,则指向实例本身。
之后,对一些状态的初值进行初始化:
// 向子组件传递的数据选项
vm._provided = parent ? parent._provided : Object.create(null)
// 用于监听的watcher实例对象
vm._watcher = null
// keep-alive的组件状态属性
vm._inactive = null
// 同上
vm._directInactive = false
// 标记是否完成挂载
vm._isMounted = false
// 标记是否完成销毁
vm._isDestroyed = false
// 标记是否在销毁过程中(在beforeDestory和destoryed两个生命周期之间)
vm._isBeingDestroyed = false
生命周期的调用
在Vue实例初始化这篇文章中,我们提到过,传入的生命周期方法会合并成一个数组,因此在触发相关生命周期的时候,需要将这个数组里的所有方法全部进行调用:
// 源码文件:src\core\instance\lifecycle.ts
export function callHook(
vm: Component,
hook: string,
args?: any[],
setContext = true
) {
pushTarget()//依赖收集,依赖管理入栈处理,避免重复依赖
const prev = currentInstance
setContext && setCurrentInstance(vm)
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, args || null, vm, info)
}
}
if (vm._hasHookEvent) {
vm.$emit('hook:' + hook)
}
setContext && setCurrentInstance(prev)
popTarget()//同上,依赖收集,依赖管理出栈处理,避免重复依赖
}
执行数组中所有钩子方法的时候进行了一层封装,主要就是做一个错误捕捉:
// 源码文件:src\core\util\error.ts
export function invokeWithErrorHandling(
handler: Function,
context: any,
args: null | any[],
vm: any,
info: string
) {
let res
try {
res = args ? handler.apply(context, args) : handler.call(context)
if (res && !res._isVue && isPromise(res) && !(res as any)._handled) {
res.catch(e => handleError(e, vm, info + ` (Promise/async)`))
// issue #9511
// avoid catch triggering multiple times when nested calls
;(res as any)._handled = true
}
} catch (e: any) {
handleError(e, vm, info)
}
return res
}