注册钩子函数
const createHook = (lifecycle) => (hook, target = currentInstance) =>
// post-create lifecycle registrations are noops during SSR (except for serverPrefetch)
(!isInSSRComponentSetup || lifecycle === "sp" /* SERVER_PREFETCH */) &&
injectHook(lifecycle, hook, target);
const onBeforeMount = createHook("bm" /* BEFORE_MOUNT */);
const onMounted = createHook("m" /* MOUNTED */);
const onBeforeUpdate = createHook("bu" /* BEFORE_UPDATE */);
const onUpdated = createHook("u" /* UPDATED */);
const onBeforeUnmount = createHook("bum" /* BEFORE_UNMOUNT */);
const onUnmounted = createHook("um" /* UNMOUNTED */);
const onServerPrefetch = createHook("sp" /* SERVER_PREFETCH */);
const onRenderTriggered = createHook("rtg" /* RENDER_TRIGGERED */);
const onRenderTracked = createHook("rtc" /* RENDER_TRACKED */);
function onErrorCaptured(hook, target = currentInstance) {
injectHook("ec" /* ERROR_CAPTURED */, hook, target);
}
injected函数实现如下
function injectHook(type, hook, target = currentInstance, prepend = false) {
const hooks = target[type] || (target[type] = [])
// 封装 hook 钩子函数并缓存
const wrappedHook = hook.__weh ||
(hook.__weh = (...args) => {
if (target.isUnmounted) {
return
}
// 停止依赖收集
pauseTracking()
// 设置 target 为当前运行的组件实例
setCurrentInstance(target)
// 执行钩子函数
// callWithAsyncErrorHandling方法执行注册的hook钩子函数, 执行完毕后设置当前运行组件实例为null, 并恢复依赖收集
const res = callWithAsyncErrorHandling(hook, target, type, args)
setCurrentInstance(null)
// 恢复依赖收集
resetTracking()
return res
})
if (prepend) {
hooks.unshift(wrappedHook)
}
else {
hooks.push(wrappedHook)
}
}
执行顺序
function registerLifecycleHook(register, hook) {
if (isArray(hook)) {
hook.forEach(_hook => register(_hook.bind(publicThis)));
}
else if (hook) {
register(hook.bind(publicThis));
}
}
registerLifecycleHook(onBeforeMount, beforeMount);
registerLifecycleHook(onMounted, mounted);
registerLifecycleHook(onBeforeUpdate, beforeUpdate);
registerLifecycleHook(onUpdated, updated);
registerLifecycleHook(onActivated, activated);
registerLifecycleHook(onDeactivated, deactivated);
registerLifecycleHook(onErrorCaptured, errorCaptured);
registerLifecycleHook(onRenderTracked, renderTracked);
registerLifecycleHook(onRenderTriggered, renderTriggered);
registerLifecycleHook(onBeforeUnmount, beforeUnmount);
registerLifecycleHook(onUnmounted, unmounted);
registerLifecycleHook(onServerPrefetch, serverPrefetch);
onBeforeUpdate 和 onUpdated
onBeforeUpdate注册的beforeUpdate会在组件更新之前执行, onUpdated会在组件更新之后执行.
不要在 updated 钩子函数中更改数据,因为这样会再次触发组件更新,导致无限递归更新
父组件的更新不一定会导致子组件的更新,因为 Vue.js 的更新粒度是组件级别的
onBeforeUnmount 和 onUnmounted
onBeforeUnmount 注册的 beforeUnMount 钩子函数会在组件销毁之前执行
onUnmounted 注册的 unmounted 钩子函数会在组件销毁之后执行
const unmountComponent = (instance, parentSuspense, doRemove) => {
const { bum, effects, update, subTree, um } = instance
// 执行 beforeUnmount 钩子函数
if (bum) {
invokeArrayFns(bum)
}
// 清理组件引用的 effects 副作用函数
if (effects) {
for (let i = 0; i < effects.length; i++) {
stop(effects[i])
}
}
// 如果一个异步组件在加载前就销毁了,则不会注册副作用渲染函数
if (update) {
stop(update)
// 调用 unmount 销毁子树
unmount(subTree, instance, parentSuspense, doRemove)
}
// 执行 unmounted 钩子函数
if (um) {
queuePostRenderEffect(um, parentSuspense)
}
}
function stop(effect) {
if (effect.active) {
cleanup(effect);
if (effect.options.onStop) {
effect.options.onStop();
}
effect.active = false;
}
}
onErrorCaptured
组件销毁的逻辑比较简单, 就是清理组件实例上绑定的effect副作用函数和注册的副作用渲染函数update以及调用unmount销毁子树.
函数会从当前宝座的组件的父组件实例开始, 尝试去查找注册errorCaptured钩子函数, 如果有则遍历判断errorCaptured钩子函数是否为true, 如果是则说明这个错误已经正确处理, 否则会通过logError向控制台抛出错误.
onRenderTracked 状态跟踪
onRenderTracked直译过来就是状态跟踪,它会跟踪页面上所有响应式变量和方法的状态,也就是我们用return返回去的值,它都会跟踪。只要页面有update的情况,它就会跟踪,然后生成一个event对象,我们通过event对象来查找程序的问题所在。
onRenderTriggered 状态触发
onRenderTriggered直译过来是状态触发,它不会跟踪每一个值,而是给你变化值的信息,并且新值和旧值都会给你明确的展示出来。
如果把onRenderTracked比喻成散弹枪,每个值都进行跟踪,那onRenderTriggered就是狙击枪,只精确跟踪发生变化的值,进行针对性调试。
对 event 对象属性的详细介绍:
- key 那边变量发生了变化
- newValue 更新后变量的值
- oldValue 更新前变量的值
- target 目前页面中的响应变量和函数