面试
面试前端时,我们经常会被问到:你能讲一下vue的生命周期吗?
你可能会这么说:vue的生命周期有beforeCreate、created、beforeMount、mounted......
面试官可能会告诉你:可以了,回去等面试结果通知吧!!!
如果这时你还能讲一讲vue生命周期的执行原理,这岂不是能在面试中有所加分,不然,你拿什么与别人与众不同
要想深入了解vue的生命周期,首先让我们看到vue源码
Vue生命周期源码解析
callHook函数
在vue源码lifecycle.ts文件下有这么一个方法:
scss
//lifecycle.ts
export function callHook(
vm: Component,
hook: string,
args?: any[],
setContext = true
) {
// #7573 disable dep collection when invoking lifecycle hooks
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()
}
这个方法被多处引入使用
_init方法中:
mountComponent方法中:
callUpdatedHooks方法中:
$destroy方法中:
其实这些callHook方法都是在执行相应的生命周期钩子函数,即:执行所提供的生命周期回调函数
callHook函数形参:
vm:Component(当前组件实例对象)
hook:string(生命周期名,如:"beforeCreate","created","beforeMount","mounted"......)
args:any[](执行生命周期钩子函数时,传递的参数)
setContext:Boolean(默认为true,为真时设置currentInstance为vm实例对象,为假时不进行此操作)
callHook函数是如何获取并执行生命周期钩子函数的?
回到callHook函数中,const handlers = vm.$options[hook] ,handlers变量(存储着对应生命周期钩子函数的数组)
继续往下
typescript
//lifecycle.ts
//callHook函数中
if (handlers) {
for (let i = 0, j = handlers.length; i < j; i++) {
invokeWithErrorHandling(handlers[i], vm, args || null, vm, info)
}
}
//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
}
在callHook 函数中遍历handlers (生命周期钩子函数数组),遍历至每一项时会执行函数:invokeWithErrorHandling(handlers[i], vm, args || null, vm, info)
handlers[i]为生命周期钩子函数
vm为当前组件实例对象
args为传递给钩子函数的参数
info为string:"生命周期名 hook"
然后,通过invokeWithErrorHandling 函数中res = args ? handler.apply(context, args) : handler.call(context) 执行相应生命周期钩子函数
handlers生命周期钩子函数为何是数组?
handlers中包含通过minxin混入的生命周期钩子函数和组件的生命周期钩子函数,所以它是一个数组 ,handlers[]:Function
vm.$options[hook]是在何时初始化生命周期钩子函数的?
_init函数中:
swift
//init.ts
//Vue.prototype._init中
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options as any)
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor as any),
options || {},
vm
)
}
如果当前初始化是new Vue实例,会执行*vm. <math xmlns="http://www.w3.org/1998/Math/MathML"> o p t i o n s = m e r g e O p t i o n s ( r e s o l v e C o n s t r u c t o r O p t i o n s ( v m . c o n s t r u c t o r a s a n y ) , o p t i o n s ∣ ∣ , v m ) ∗ ,并且生命周期钩子函数数组挂载在 v m . options = mergeOptions(resolveConstructorOptions(vm.constructor as any),options || {},vm)* ,并且生命周期钩子函数数组挂载在vm. </math>options=mergeOptions(resolveConstructorOptions(vm.constructorasany),options∣∣,vm)∗,并且生命周期钩子函数数组挂载在vm.options上
如果是子组件初始化,会执行initInternalComponent(vm, options as any) ,并且生命周期钩子函数数组会挂载在vm.$options原型链上
vue组件@hook:xxx事件
callHook 函数在执行完生命周期钩子函数(invokeWithErrorHandling)后会执行hook:xxx事件
bash
//lifecycle.ts
//callHook函数中
if (vm._hasHookEvent) {
vm.$emit('hook:' + hook)
}
例如我们要在父组件中监听子组件mounted钩子函数执行完,我们可以这样做:
1、子组件与父组件通过on与emit进行发布订阅
2、在子组件上注册hook:xxx事件:<compontent @hook:mounted="action" />,action事件会在执行完mounted生命周期钩子函数后被调用
如果觉得内容对你有帮助或者有所成长,麻烦请点个小小的赞加收藏,谢谢。^o^