概述
生命周期在Vue3
中是一个很重要的概念,它允许我们在特定的时间点执行一些代码,比如在组件创建、更新和销毁时执行一些操作。
生命周期函数分为以下几种:
- 1.
onBeforeMount
: 在组件挂载之前执行 - 2.
onMounted
: 在组件挂载之后执行 - 3.
onBeforeUpdate
: 在组件更新之前执行 - 4.
onUpdated
: 在组件更新之后执行 - 5.
onBeforeUnmount
: 在组件销毁之前执行 - 6.
onUnmounted
: 在组件销毁之后执行 - 7.
onServerPrefetch
: 在服务器端渲染时执行 - 8.
onRenderTracked
: 在组件渲染时执行 - 9.
onRenderTriggered
: 在组件渲染时执行 - 10.
onErrorCaptured
: 在组件发生错误时执行
Vue3的生命周期运行示意图如下
源码分析
Vue3
中的上述这些生命周期钩子函数的实现大同小异,主要都是基于hook
函数实现,在不同的时机执行对应的·hook·,其主要实现位于packages\runtime-core\dist\runtime-core.cjs.js
const createHook =
(lifecycle) =>
(hook, target = currentInstance) => {
if (!isInSSRComponentSetup || lifecycle === "sp") {
injectHook(lifecycle, (...args) => hook(...args), target);
}
};
lifecycle
为onBeforeMount
等钩子函数名称,如下枚举:,hook
就是钩子函数中的回调函数,target
为当前组件实例
export enum LifecycleHooks {
BEFORE_CREATE = 'bc',
CREATED = 'c',
BEFORE_MOUNT = 'bm',
MOUNTED = 'm',
BEFORE_UPDATE = 'bu',
UPDATED = 'u',
BEFORE_UNMOUNT = 'bum',
UNMOUNTED = 'um',
DEACTIVATED = 'da',
ACTIVATED = 'a',
RENDER_TRIGGERED = 'rtg',
RENDER_TRACKED = 'rtc',
ERROR_CAPTURED = 'ec',
SERVER_PREFETCH = 'sp',
}
在调用injectHook
时,会将同类型的hook
挂载到组件实例中,hook
会被包装一层,用于处理错误捕获和跟踪,其中也用了缓存的策略,具体实现如下:
function injectHook(type, hook, target = currentInstance, prepend = false) {
if (target) {
const hooks = target[type] || (target[type] = []);
const wrappedHook =
hook.__weh ||
(hook.__weh = (...args) => {
if (target.isUnmounted) {
return;
}
reactivity.pauseTracking(); // 暂停依赖追踪
const reset = setCurrentInstance(target);
const res = callWithAsyncErrorHandling(hook, target, type, args);
reset();
reactivity.resetTracking(); // 恢复依赖追踪
return res;
});
if (prepend) {
// 如果是前置钩子,则插入到数组的开头
hooks.unshift(wrappedHook);
} else {
hooks.push(wrappedHook);
}
return wrappedHook;
}
}
注册过程
上面阐述了hook
的创建,接下来看下生命周期钩子函数的注册,函数注册后,才方便vue3
在适当的时机调用。vue3
自定义了一个registerLifecycleHook
函数,用于注册生命周期钩子函数。如下所示:
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); // ssr内容,后面不会讲
这段代码还将钩子函数与publicThis
绑定,并将它们传递给registerLifecycleHook
函数。publickThis
是就是当前instance
的proxy
对象,用于存储当前实例的变量
instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers);
生命周期函数函数经历的阶段
vue3
是在组件初始化时进行钩子函数的注册,主要经历了这些阶段:
render
=>patch
=> processComponent
=> mountComponent
=> setupComponent
=> setupStatefulComponent
=> handleSetupResult
=>finishComponentSetup
=> applyOptions
=> registerLifecycleHook
各个阶段的作用如下:
render
:Vue
组件中定义的渲染函数,它返回一个虚拟 DOM 树描述;定义了组件的 UI 结构,它可以是模板语法或者render
函数本身,用于生成组件的虚拟 DOM。patch
:Vue
内部的核心函数,负责将虚拟 DOM 渲染成真实 DOM,并处理 DOM 的更新和变更;实现了 Vue 的响应式更新机制,确保虚拟 DOM 的变更能够高效地同步到真实 DOM 上,实现数据驱动的视图更新。processComponent
:处理组件的函数,可能包括组件的初始化、更新等过程。在组件的生命周期中,processComponent 负责管理组件的各个阶段,例如数据的准备、事件的绑定等,确保组件能够正常工作并响应数据变化。mountComponent
:挂载组件到 DOM 上的过程,包括创建组件实例、初始化数据等。mountComponent
是组件生命周期中的重要阶段,它负责将组件实例化并将其挂载到页面的 DOM 元素上,使得组件能够被用户访问和操作。setupComponent
:设置组件的函数,可能包括组件的配置项、参数处理等。setupComponent
阶段用于初始化组件的配置和参数,为后续的生命周期钩子、事件处理和数据绑定等操作做准备。setupStatefulComponent
:设置有状态组件的函数,用于处理有状态组件的状态初始化和管理。对于有状态的组件(即有响应式数据的组件),setupStatefulComponent
负责初始化组件的状态数据,并确保这些数据的响应式更新机制能够正常运作。handleSetupResult
:处理设置结果的函数,可能包括对组件状态和属性的初始化。主要用于处理setup()
函数的返回结果,确保组件的状态、计算属性等被正确地初始化和配置。finishComponentSetup
:完成组件设置的函数,确保组件已经初始化并准备好渲染。在组件初始化阶段的最后,finishComponentSetup
确保所有的组件设置和初始化工作已经完成,组件可以安全地开始渲染和更新。applyOptions
:应用组件选项的函数,包括组件的配置项、生命周期钩子等。applyOptions
用于将组件定义的选项(如 data、methods、computed 等)应用到组件实例上,以及注册和管理组件的生命周期钩子函数,确保组件在各个生命周期阶段能够执行相应的逻辑。
生命周期钩子函数的调用时机
onBeforeMount
在mountComponent
阶段,会调用setupRenderEffect
函数,这个函数顾名思义就是设置组件的effect
,用于设置和管理组件的渲染效果,实现vue3
组件的响应式更新机制,确保当组件的响应式数据发生变化时,能够自动重新渲染组件的视图
setupRenderEffect
函数内部会new ReactiveEffect
实例,并赋值给instance.effect
,并调用componentUpdateFn
函数执行。而在componentUpdateFn
中会先判断组件是否已经被挂载,如果已经挂载,则处理更新逻辑,否则进行挂载逻辑。
当组件未被挂载时,instance.isMounted
为false
,Vue3
会从instance
中获取注册钩子函数bm
,若它存在,在调用invokeArrayFns
进行遍历调用。
onMounted
onMounted
钩子函数同onBeforeMount
一样,在组件未被挂载时,从instance
中解构注册的钩子函数m
,通过queuePostRenderEffect
进入调度器schedule
,在组件挂载完成时调用flushPostFlushCbs
,其内部会根据每一个job
的id
进行排序,然后依次执行。
onBeforeUpdate
onBeforeUpdate
钩子函数和onBeforeMount
函数类似,不同的是onBeforeUpdate
是在组件被挂载后即instance.isMounted
为true
时,在组件发生的响应式数据变化时,解构钩子函数bu
,被调用
onUpdated
onUpdated
钩子函数和onMounted
函数类似,不同的是onUpdated
是在组件被挂载后即instance.isMounted
为true
时,在组件发生的响应式数据变化时,解构钩子函数u
,被调用
onBeforeUnmount
onBeforeUnmount
在组件卸载前会调用,对应的注册的钩子函数变量名为bum
,会在unmountComponent
函数里调用。而unmountComponent
函数会在函数unmount
中判断shapeFlag
,若其为组件类型则调用。通过invokeArrayFns
进行遍历执行
onUnmounted
onUnmounted
在组件卸载后调用,对应的注册的钩子函数变量名为um
,组件在卸载时先调用onBeforeUnmount
钩子,如果instance.update
存在,则调用unmount
方法,然后调用queuePostRenderEffect
将um
加入到调度器中,在组件卸载完成后调用flushPostFlushCbs
,执行um
钩子函数。