前言
什么是HookEvent?实际上是Vue中提供的一类与生命周期相关的事件,这些事件的触发与生命周期函数调用存在关联。我们知道Vue2.x提供了一些生命周期函数,用于提供开发者对于特殊逻辑点的逻辑处理。本文内容实际上分为2个部分:
- 生命周期函数调用
- HookEvent
生命周期函数调用
实际上Vue源码对于生命周期对应函数的调用都是通过callHook函数来处理的,该部分逻辑实际上关注点也就几个:
function callHook (vm, hook) {
// #7573 disable dep collection when invoking lifecycle hooks
pushTarget();
var handlers = vm.$options[hook];
var info = hook + " hook";
if (handlers) {
for (var i = 0, j = handlers.length; i < j; i++) {
invokeWithErrorHandling(handlers[i], vm, null, vm, info);
}
}
if (vm._hasHookEvent) {
vm.$emit('hook:' + hook);
}
popTarget();
}
实际上通过上面源码有3点信息:
- 生命周期函数都是存在vue实例的$options属性中的
- 函数的处理调用invokeWithErrorHandling函数
- 当_hasHookEvent存在时会触发hook:created类似的事件
invokeWithErrorHandling
实际上通过该函数名称可以大概知道该函数的功能:带有错误处理功能的调用。实际上该函数的功能就是如此,对外的生命周期函数需要主动捕获其过程发生的错误,不然回会导致整个Vue实例生成过程终止。具体源码逻辑如下:
function invokeWithErrorHandling (
handler,
context,
args,
vm,
info
) {
var res;
try {
// 执行生命周期函数
res = args ? handler.apply(context, args) : handler.call(context);
if (res && !res._isVue && isPromise(res) && !res._handled) {
res.catch(function (e) {
return handleError(e, vm, info + " (Promise/async)");
});
// issue #9511
// avoid catch triggering multiple times when nested calls
res._handled = true;
}
} catch (e) {
handleError(e, vm, info);
}
return res
}
HookEvent
HookEvent是Vue定义的与生命周期相关的事件类型,其具体形式是:
hook:生命周期名称
生命周期函数调用中(callHook函数中)有该类事件的触发入口,即:
if (vm._hasHookEvent) {
vm.$emit('hook:' + hook);
}
从上面逻辑中可知_hasHookEvent是该类事件触发的前置条件,而该实例属性的应用在Vue源码中有3处地方:
- initEvents函数中赋值操作
- 事件注册实例方法$on中赋值操作
- callHoook函数中判断_hasHookEvent是否为true
实际上initEvents是初始化事件相关的处理,这里_hasHookEvent并赋值为false,核心逻辑在事件注册实例方法$on中,具体逻辑如下:
var hookRE = /^hook:/;
Vue.prototype.$on = function (event, fn) {
var vm = this;
if (Array.isArray(event)) {
for (var i = 0, l = event.length; i < l; i++) {
vm.$on(event[i], fn);
}
} else {
(vm._events[event] || (vm._events[event] = [])).push(fn);
// optimize hook:event cost by using a boolean flag marked at registration
// instead of a hash lookup
if (hookRE.test(event)) {
vm._hasHookEvent = true;
}
}
return vm
};
从上面可以知道_hasHookEvent开启的条件是:
$on注册事件是hook:开头的并且是单个事件处理程序
从上面的整体逻辑可知HookEvent相关信息:
当实例注册了hook:开头的事件后,会在生命周期钩子函数调用时触发对应的hook事件,例如hook:created、hook:mounted等
HookEvent应用场景
从上面可知道hook event开启的条件以及事件执行的事件点,那么为什么会存在HookEvent这样特殊的事件呢?
在日常开发过程中,经常是直接在vue组件中显式调用生命周期函数,例如:
mounted() {
// 相关处理
}
HookEvent逻辑的存在就允许通过js来动态添加生命周期,例如:
created() {
this.$on('hook:mounted', function() {});
}
但是这样的场景不常见而且也是比较怪异的。通过HookEvent的相关逻辑分析,更常见合理的场景应该是:
从外部注入生命周期事件
比如第三方的Vue组件el-select,我想从外部注入mounted生命周期函数。
<el-select ref="select" @hook:mounted="callback"></el-select>
// 获取select实例注册
this.$refs['select'].$on('hook:mounted', function() {})
通过外部注入HookEvent可以在组件对应的生命周期时间点触发外部相关逻辑。目前个人倒是没有遇到这样的实际场景,可见这种场景在日常业务开发中是并不多见的。
总结
HookEvent定义:
是指定义以生命周期命名的固定形式的特殊事件,形式如:hook:created、hook:mounted等。
该类事件触发时机:
组件对应的生命周期函数执行后
使用场景:
从外部注入对应子组件的HookEvent,在指定的生命周期执行时间点执行相关的外部逻辑