2026.01.28
组件生命周期 -> life cycle
在之前的章节中,我们已经实现了组件的挂载和更新机制。Vue 提供了一套完整的 生命周期系统,允许开发者在组件的不同阶段执行特定的逻辑。生命周期钩子主要分为三个阶段:挂载、更新和卸载。
在之前的章节中,我们已经实现了组件的挂载和更新机制。Vue 提供了一套完整的 生命周期系统,允许开发者在组件的不同阶段执行特定的逻辑。生命周期钩子主要分为三个阶段:挂载、更新和卸载。
生命周期概述
Vue 支持以下生命周期钩子:
1. 挂载阶段
onBeforeMount:组件挂载前调用onMounted:组件挂载完成后调用
2. 更新阶段
onBeforeUpdate:组件更新前调用onUpdated:组件更新完成后调用
3. 卸载阶段
onBeforeUnmount:组件卸载前调用onUnmounted:组件卸载完成后调用
生命周期枚举定义
首先在 packages/runtime-core/src/apiLifecycle.ts 中定义生命周期钩子类型:
export enum LifecycleHooks {
BEFORE_MOUNT = 'bm',
MOUNTED = 'm',
BEFORE_UPDATE = 'bu',
UPDATED = 'u',
BEFORE_UNMOUNT = 'bum',
UNMOUNTED = 'um'
}
设计要点:
- 使用简写形式的键值存储在组件实例上,减少内存占用
- 枚举类型确保类型安全,避免硬编码字符串错误
生命周期钩子函数实现
createHook 基础实现
每个生命周期钩子都通过 createHook 工厂函数创建:
const onBeforeMount = createHook(LifecycleHooks.BEFORE_MOUNT)
const onMounted = createHook(LifecycleHooks.MOUNTED)
const onBeforeUpdate = createHook(LifecycleHooks.BEFORE_UPDATE)
const onUpdated = createHook(LifecycleHooks.UPDATED)
const onBeforeUnMount = createHook(LifecycleHooks.BEFORE_UNMOUNT)
const onUnMounted = createHook(LifecycleHooks.UNMOUNTED)
export {
onBeforeMount,
onBeforeUnMount,
onBeforeUpdate,
onMounted,
onUnMounted,
onUpdated
}
createHook 工厂函数
function createHook(type: LifecycleHooks) {
return (hook, target = getCurrentInstance()) => {
injectHook(target, hook, type)
}
}
设计要点:
- 返回一个闭包函数,可以调用并传入 hook 回调
- 默认获取当前组件实例,也支持手动指定 target(用于组合式函数)
- 通过
injectHook将 hook 注入到对应的生命周期数组中
injectHook 注入机制
function injectHook(target, hook, type) {
if (target[type] == null) {
target[type] = []
}
const _hook = () => {
setCurrentInstance(target)
hook()
unsetCurrentInstance()
}
target[type].push(_hook)
}
逻辑说明:
- 数组存储:每个生命周期对应一个数组,支持多个 hook(如 VueUse 等组合式函数场景)
- 实例上下文:在 hook 执行时重新设置当前实例,确保
getCurrentInstance在生命周期内可用 - 环境恢复:hook 执行完成后清理当前实例,避免上下文污染
triggerHooks 触发机制
function triggerHooks(instance, type: LifecycleHooks) {
const hooks = instance[type]
if (hooks) {
hooks.forEach(hook => hook())
}
}
逻辑说明:
- 获取对应类型的 hook 数组
- 遍历并执行所有 hook
- 按注册顺序依次执行
生命周期使用示例
const Comp = {
setup() {
onMounted(() => {
console.log('onMounted')
})
return () => {
return h('div', 'hello')
}
}
}
集成到组件更新流程
componentUpdateFn 集成
现在将生命周期钩子集成到 componentUpdateFn 中:
function componentUpdateFn() {
const { isMounted, vnode, render, subTree, next } = instance
if (!isMounted) {
// 挂载前
triggerHooks(instance, LifecycleHooks.BEFORE_MOUNT)
const newSubTree = render.call(instance.proxy)
patch(null, newSubTree, container, anchor)
vnode.el = newSubTree.el
instance.subTree = newSubTree
instance.isMounted = true
// 挂载后
triggerHooks(instance, LifecycleHooks.MOUNTED)
}
else {
if (next) {
updateComponentPreRender(instance, next)
}
else {
next = vnode
}
// 更新前
triggerHooks(instance, LifecycleHooks.BEFORE_UPDATE)
const prevSubTree = instance.subTree
const newSubTree = render.call(instance.proxy)
patch(prevSubTree, newSubTree, container, anchor)
next.el = newSubTree.el
instance.subTree = newSubTree
// 更新后
triggerHooks(instance, LifecycleHooks.UPDATED)
}
}
逻辑说明:
- 挂载阶段:
- 执行
onBeforeMounthook - 渲染并挂载子树
- 执行
onMountedhook
- 执行
- 更新阶段:
- 更新组件预渲染
- 执行
onBeforeUpdatehook - diff patch 更新子树
- 执行
onUpdatedhook
组件卸载生命周期
unmount 函数扩展
之前的 unmount 函数只处理了 children 的卸载,现在需要添加组件卸载处理:
function unmount(vnode) {
const { type, shapeFlag } = vnode
if (shapeFlag & ShapeFlags.COMPONENT) {
unmountComponent(vnode.component)
}
else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
unmountChildren(vnode.children)
}
hostRemove(vnode.el)
}
逻辑说明:
- 根据不同的
shapeFlag判断卸载类型 - 组件类型调用
unmountComponent - 数组子节点调用
unmountChildren - 最终移除 DOM 元素
unmountComponent 实现
function unmountComponent(instance) {
// 卸载前
triggerHooks(instance, LifecycleHooks.BEFORE_UNMOUNT)
unmount(instance.subTree)
// 卸载后
triggerHooks(instance, LifecycleHooks.UNMOUNTED)
}
逻辑说明:
- 卸载前:执行
onBeforeUnmounthook - 卸载子树:递归卸载组件的所有子节点
- 卸载后:执行
onUnmountedhook
完整测试用例
<div id="app"></div>
<script type="module">
import {
h,
createApp,
ref,
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnMount,
onUnMounted,
} from '../dist/vue.esm.js'
const Child = {
setup(props) {
onBeforeMount(() => {
console.log('Child onBeforeMount')
})
onMounted(() => {
console.log('Child onMounted')
})
onBeforeUpdate(() => {
console.log('Child onBeforeUpdate')
})
onUpdated(() => {
console.log('Child onUpdated')
})
onBeforeUnMount(() => {
console.log('Child onBeforeUnmount')
})
onUnMounted(() => {
console.log('Child onUnmounted')
})
return () => h('span', props.count)
}
}
const Comp = {
setup() {
const p = document.querySelector('#p')
console.log('setup:', p) // null
onBeforeMount(() => {
const p = document.querySelector('#p')
console.log('onBeforeMount:', p) // null
})
onMounted(() => {
const instance = getCurrentInstance()
console.log("🚀 ~ instance:", instance)
const p = document.querySelector('#p')
console.log('onMounted:', p) // <p>
})
onBeforeUpdate(() => {
console.log('onBeforeUpdate')
})
onUpdated(() => {
console.log('onUpdated')
})
onBeforeUnMount(() => {
console.log('onBeforeUnmount')
})
onUnMounted(() => {
console.log('onUnmounted')
})
const count = ref(0)
return () => h('div', [
h('p', {
id: 'p',
onClick() {
count.value++
}
}, 'parent'),
h(Child, { count: count.value })
])
}
}
const app = createApp(Comp)
app.mount('#app')
setTimeout(() => {
app.unmount()
}, 2000)
</script>
测试验证:
- 挂载流程:验证
onBeforeMount→onMounted的调用顺序 - 更新流程:点击父组件触发更新,验证
onBeforeUpdate→onUpdated的调用顺序 - 卸载流程:2 秒后卸载应用,验证
onBeforeUnmount→onUnmounted的调用顺序 - DOM 访问:验证
onMounted阶段能够正确访问到 DOM 元素
总结
至此,我们完成了 Vue 组件生命周期系统的完整实现:
1. 生命周期枚举系统
- 定义了六大生命周期钩子类型
- 使用简写键值优化存储空间
- 确保类型安全
2. Hook 注入机制
createHook:工厂函数创建生命周期钩子injectHook:将 hook 注入到组件实例的对应数组中- 支持多 hook 场景(组合式函数)
3. 实例上下文管理
- 在 hook 执行时重新设置当前实例
- 确保
getCurrentInstance在生命周期内可用 - 执行完成后清理上下文,避免污染
4. 生命周期触发
triggerHooks:统一的生命周期触发函数- 按注册顺序依次执行所有 hook
- 集成到挂载、更新、卸载流程中
5. 完整的卸载流程
- 扩展
unmount函数支持组件卸载 - 实现
unmountComponent处理组件卸载逻辑 - 确保子树递归卸载和生命周期正确执行
这套生命周期系统为组件提供了完整的生命周期管理能力,是 Vue 组件化开发的重要基础。通过生命周期钩子,开发者可以在组件的不同阶段执行初始化、清理、监听等逻辑,实现更复杂的业务需求。