前言
在之前的几篇文章中,都有提到 vue
中调用生命周期钩子时是通过 callHook()
方法进行调用的,比如在初始化篇章中调用 beforeCreate
和 created
生命周期钩子方式如下:
那么接下来一起来了解下到底什么是 Hook Event
?
Hook Event
是什么?
Hook Event
在 vue
的官方文档中并没有提及到,这也是很多人很少或者几乎没有使用到 Event Hook
的主要原因,但这不代表不可以使用.
Vue
中提供了对应的生命周期钩子,方便开发者在特定的时间节点进行对应的逻辑处理,比如:在组件的 mounted
生命周期钩子中,准备组件渲染所需要的数据.
而 Hook Event
是通过 Vue
中 自定义事件 + 生命周期钩子 的方式,实现从组件外部为组件注入额外生命周期方法的功能.
怎么用?
这里直接通过一个简单的例子来快速了解 Hook Event
的使用.
首先通过 Vue.component()
注册了 ListView
主要用于列表渲染,在其 mounted
生命周期钩子中通过 setTimeout()
模仿接口请求,并在模板中进行使用 <list-view @hook:mounted="mountedAction"></list-view>
,接着通过 @hook:mounted="mountedAction"
向列表组件组件中注入了外部定义的 mounted
钩子中需要执行的事件,此时控制台上的输出结果为:
这就证明了 Hook Event
就是通过 自定义事件 + 生命周期钩子 的方式,实现从组件外部为组件注入额外生命周期方法.
具体代码如下:
// HTML 模板
<div id="app">
<list-view @hook:mounted="mountedAction"></list-view>
</div>
// JS 逻辑
<script>
// 第三方 vue 组件
Vue.component('ListView', {
template:`<ul class="list-view">
<li v-for="item in list">{{ item }}</li>
</ul>`,
data(){
return {
list: []
}
},
mounted(){
console.log("mounted in ListView...");
setTimeout(()=>{
this.list = [1,2,3,4,5,6];
},1000);
}
});
new Vue({
el: '#app',
methods:{
mountedAction(){
console.log("mountedAction from parent...");
}
}
});
</script>
callHook() 方法
文件位置:src\core\instance\lifecycle.js
callHook
中通过 vm._hasHookEvent
标识判断是否存在 Hook Event
,而这个标识是在 Vue.prototype.$on
实例方法中进行定义的,详情可看 eventsMixin(Vue) 方法.
export function callHook (vm: Component, hook: string) {
// #7573 disable dep collection when invoking lifecycle hooks
// 调用前打开依赖收集
pushTarget()
// 从组件配置项中获取对应的生命周期钩子,类型为数组
const handlers = vm.$options[hook]
const info = `${hook} hook`
if (handlers) {
for (let i = 0, j = handlers.length; i < j; i++) {
// 通过 apply 或 call 的方式调用生命周期函数
invokeWithErrorHandling(handlers[i], vm, null, vm, info)
}
}
// 如果有 HookEvent 事件传入,则通过 $emit 调用这个事件,如 hook:mounted
if (vm._hasHookEvent) {
// 本质就是执行 vm._events['hook:mounted'] 数组中的所有处理函数
vm.$emit('hook:' + hook)
}
// 调用完成后关闭依赖收集
popTarget()
}
invokeWithErrorHandling() 方法
文件位置:src\core\util\error.js
/*
1. 使用 try catch 包裹生命周期钩子中的逻辑,便于进行异常捕获
2. 调用生命周钩子:有 args 参数通过 apply 调用,否则通过 call 调用
3. 返回调用结果
*/
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._handled) {
res.catch(e => handleError(e, vm, info + ` (Promise/async)`))
// issue #9511
// 避免在嵌套调用时多次触发 catch
res._handled = true
}
} catch (e) {
// 异常处理
handleError(e, vm, info)
}
return res
}
总结
Hook Event 原理是什么?
首先 Hook Event
的作用实现从组件外部为组件注入额外生命周期方法的功能,主要是通过 自定义事件 + 生命周期钩子 的方式实现的.
vue
在处理组件如<comp @hook:lifecycle="customMethod" />
时,会将这个事件通过vm.$on() 即 Vue.prototype.$on()
方法进行监听处理,并且遇到格式为hook:xx
的事件时,会将vm._hasHookEvent
置为true
,表示该组件有Hook Event
- 在通过
callHook()
方法调用生命周期钩子时,会以循环的方式执行组件上对应的生命周期钩子 - 在执行完后组件上的生命周期钩子后,会通过
vm._hasHookEvent
标识判断当前组件是否存在Hook Event
,如果存在就通过vm.$emit('hook:xxx') 即 Vue.prototype.$emit()
方法调用vm._events['hook:xxx']
事件上的所有处理函数