生命周期
# 概述
生命周期在Vue3
中是一个很重要的概念,它允许我们在特定的时间点执行一些代码,比如在组件创建、更新和销毁时执行一些操作。
生命周期函数分为以下几种:
- 1.
onBeforeMount
: 在组件挂载之前执行 - 2.
onMounted
: 在组件挂载之后执行 - 3.
onBeforeUpdate
: 在组件更新之前执行 - 4.
onUpdated
: 在组件更新之后执行 - 5.
onBeforeUnmount
: 在组件销毁之前执行 - 6.
onUnmounted
: 在组件销毁之后执行 - 7.
onServerPrefetch
: 在服务器端渲染时执行 - 8.
onRenderTracked
: 在组件渲染时执行 - 9.
onRenderTriggered
: 在组件渲染时执行 - 10.
onErrorCaptured
: 在组件发生错误时执行
# 源码分析
Vue3
中的上述这些生命周期钩子函数的实现大同小异,主要都是基于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);
}
};
2
3
4
5
6
7
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',
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
在调用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;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 注册过程
上面阐述了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内容,后面不会讲
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
这段代码还将钩子函数与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
钩子函数。