Vue 3 生命周期钩子(Hooks)完整指南
原文来自 2020 年的 Vue 3 早期文章。本文保留原文从 Options API 迁移到 Composition API、按创建/挂载/更新/卸载/KeepAlive 介绍 hook 的结构,并补充 Vue 3.5 的
onServerPrefetch、onWatcherCleanup、Suspense、调度时机和错误边界。
一、生命周期总览
每个组件实例都会经历创建、挂载、更新和卸载等阶段。常用 hook 可以按下表理解:
| 阶段/能力 | Options API | Composition API |
|---|---|---|
| 创建 | beforeCreate、created | setup() / <script setup> |
| 挂载 | beforeMount、mounted | onBeforeMount、onMounted |
| 更新 | beforeUpdate、updated | onBeforeUpdate、onUpdated |
| 卸载 | beforeUnmount、unmounted | onBeforeUnmount、onUnmounted |
| KeepAlive | activated、deactivated | onActivated、onDeactivated |
| 错误 | errorCaptured | onErrorCaptured |
| 调试 | renderTracked、renderTriggered | onRenderTracked、onRenderTriggered |
| SSR | serverPrefetch | onServerPrefetch |
“4 个阶段、8 个主 hook”只是帮助入门的简化分类,不是 Vue 官方固定的生命周期数量。setup() 承担 Composition API 的创建阶段逻辑,Composition API 没有 onBeforeCreate 和 onCreated。
原文的生命周期示意图已下载到本地:

二、在 Options API 中使用
生命周期 hook 直接写在组件选项中:
<script>
export default {
mounted() {
console.log('mounted!')
},
updated() {
console.log('updated!')
},
}
</script>
Options API 在 Vue 3 中仍然受支持。如果旧项目使用的是 Vue 2,主要迁移差异是:
beforeDestroy -> beforeUnmount
destroyed -> unmounted
Vue 3 的 Options API 还可以使用 serverPrefetch、renderTracked、renderTriggered 和 KeepAlive 相关 hook。
三、在 Composition API 中使用
Composition API 需要从 vue 导入生命周期函数:
<script setup>
import { onMounted, onUnmounted } from 'vue'
onMounted(() => {
console.log('mounted in Composition API')
})
onUnmounted(() => {
console.log('cleanup')
})
</script>
常用映射如下:
beforeCreate / created -> setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
activated -> onActivated
deactivated -> onDeactivated
errorCaptured -> onErrorCaptured
serverPrefetch -> onServerPrefetch
这里的箭头表示“常见迁移方向”,不是说 setup() 在执行时机和 created 完全相同。setup() 在 Options API 的 beforeCreate 之前执行,并且可以在其中同步注册其他 Composition API hook。
生命周期注册必须同步发生:
<script setup>
import { onMounted } from 'vue'
// 正确:setup 同步执行期间注册
onMounted(() => {})
// 不推荐:异步回调执行时已没有当前 active instance
setTimeout(() => {
// onMounted(() => {})
}, 100)
</script>
四、Creation:组件创建阶段
beforeCreate 和 created
在 Options API 中,beforeCreate 发生在组件实例的选项初始化早期,data、computed、methods 代理还没有全部完成;created 时这些状态通常已经可以访问,但 DOM 还不存在。
export default {
data() {
return {
value: 'hello',
}
},
beforeCreate() {
console.log('beforeCreate:', this.value)
},
created() {
console.log('created:', this.value)
},
}
不要在 created 中读取真实 DOM;如果需要浏览器 API 或 DOM,应等待 mounted。如果是 SSR 数据获取,应使用框架的数据方案或 serverPrefetch / onServerPrefetch。
Composition API 中的创建逻辑
<script setup>
import { ref } from 'vue'
const value = ref('hello')
console.log(value.value)
</script>
setup() 中可以创建 refs、computed、watcher、事件处理函数,并同步注册生命周期。需要访问 DOM 的逻辑不要放在这里直接执行。
五、Mounting:访问 DOM
beforeMount / onBeforeMount
组件即将首次渲染和 patch 前调用。模板 ref 通常还没有指向真实元素:
<script setup>
import { onBeforeMount, ref } from 'vue'
const root = ref(null)
onBeforeMount(() => {
console.log(root.value) // 通常为 null
})
</script>
<template>
<div ref="root">Hello World</div>
</template>
mounted / onMounted
首次渲染完成后调用,可以读取组件自己的 DOM。普通同步子组件通常也已经完成挂载,但不等待异步组件和 Suspense 中尚未 resolve 的分支:
<script setup>
import { onMounted, ref } from 'vue'
const root = ref(null)
onMounted(() => {
console.log(root.value)
})
</script>
<template>
<div ref="root">Hello World</div>
</template>
常见用途是初始化依赖浏览器环境的第三方库、测量 DOM、添加事件监听器。SSR 期间不会调用 onMounted。
六、Updates:响应式更新阶段
beforeUpdate / onBeforeUpdate
响应式状态变化后、组件 DOM patch 之前调用:
<script setup>
import { onBeforeUpdate } from 'vue'
onBeforeUpdate(() => {
console.log('DOM is about to update')
})
</script>
updated / onUpdated
组件 DOM patch 完成后调用:
<script setup>
import { onUpdated } from 'vue'
onUpdated(() => {
console.log('DOM updated')
})
</script>
Vue 会批量处理同一轮同步状态修改,因此不要把 updated 理解为“每次赋值都执行一次”。如果只想等待某次状态变更后的 DOM,使用 nextTick():
<script setup>
import { nextTick, ref } from 'vue'
const visible = ref(false)
async function show() {
visible.value = true
await nextTick()
// 此时可以读取本轮更新后的 DOM
}
</script>
不要在 updated 中无条件修改自身依赖的响应式状态,否则可能产生无限更新。
七、Unmounting:卸载和清理
beforeUnmount / onBeforeUnmount
Vue 3 使用 onBeforeUnmount,不是 onBeforeUnmounted:
<script setup>
import { onMounted, onBeforeUnmount } from 'vue'
function handleResize() {
console.log(window.innerWidth)
}
onMounted(() => {
window.addEventListener('resize', handleResize)
})
onBeforeUnmount(() => {
window.removeEventListener('resize', handleResize)
})
</script>
事件监听器、定时器、WebSocket、IntersectionObserver 和第三方实例都应该在卸载前后清理。清理时必须使用同一个函数引用。
unmounted / onUnmounted
<script setup>
import { onUnmounted } from 'vue'
onUnmounted(() => {
console.log('component unmounted')
})
</script>
同步创建的 watcher 和 effect 通常会绑定到组件作用域并自动停止;在异步回调中创建的 watcher 可能需要手动保存 stop handle。
八、KeepAlive:激活和停用
<KeepAlive> 缓存动态组件实例,切换时通常移动缓存节点而不是重新创建组件:
<template>
<KeepAlive>
<component :is="currentTab" />
</KeepAlive>
</template>
在缓存组件中:
<script setup>
import { onActivated, onDeactivated } from 'vue'
onActivated(() => {
console.log('tab activated')
})
onDeactivated(() => {
console.log('tab deactivated')
})
</script>
需要注意:
onActivated初次挂载时也会调用,不只是“第二次激活”;onDeactivated在组件被缓存移出活动树时调用,最终卸载时也会调用;- KeepAlive 切换不等于 mounted/unmounted;
- 后代组件也会受到激活和停用影响。
原文的 KeepAlive 动图已使用当前来源站可下载的同义资源本地化:

九、Debug Hooks
Vue 3 提供两个开发环境调试 hook:
onRenderTracked:组件 render effect 跟踪响应式依赖。onRenderTriggered:已跟踪的依赖触发组件 render effect。
Composition API 写法:
<script setup>
import { onRenderTracked, onRenderTriggered } from 'vue'
onRenderTracked(event => {
console.log('tracked:', event.type, event.key)
})
onRenderTriggered(event => {
console.log('triggered:', event.type, event.key)
})
</script>
Options API 写法不是 onRenderTriggered,而是直接写 renderTriggered:
export default {
renderTracked(event) {
console.log('tracked:', event)
},
renderTriggered(event) {
console.log('triggered:', event)
},
}
这些 hook 不应写业务逻辑,也不在 SSR 中执行。event.effect 是 Vue 内部的调试 effect/subscriber 对象,不要简单解释为“重新 render 的普通函数”。
十、错误捕获
errorCaptured / onErrorCaptured 用于捕获后代组件的错误:
<script setup>
import { onErrorCaptured } from 'vue'
onErrorCaptured((error, instance, info) => {
console.error(error, info)
return false
})
</script>
错误从来源组件沿父链由近及远传播,来源组件自身不会捕获自己的错误。默认情况下,错误仍可能交给 app.config.errorHandler;返回 false 才会阻止后续传播。
错误来源可能是 setup、render、生命周期、watcher、事件处理器、指令和 transition。应记录必要信息并展示降级 UI,不要为了隐藏错误而无条件返回 false。
十一、SSR 与 onServerPrefetch
普通 DOM 生命周期在 SSR 不会调用:
不会在 SSR 执行:onMounted、onUpdated、onUnmounted、onActivated 等
需要在服务端预取数据时:
<script setup>
import { onServerPrefetch, ref } from 'vue'
const user = ref(null)
onServerPrefetch(async () => {
user.value = await fetchUser()
})
</script>
SSR renderer 会等待 onServerPrefetch 返回的 Promise。客户端 hydration 时要保证服务端输出和客户端首次渲染一致,浏览器专属代码应放在 onMounted 或客户端条件中。
十二、Suspense 和生命周期边界
Suspense 可以等待异步 setup 和异步组件:
<template>
<Suspense>
<AsyncPanel />
<template #fallback>
正在加载……
</template>
</Suspense>
</template>
pending 时,默认内容可以先渲染到隐藏容器,fallback 显示给用户;异步分支的 post-render effects 可能等待 boundary resolve 后才执行。因此:
- 父组件 mounted 不等于 Suspense 内异步后代 mounted;
- 普通 mounted 不等于接口请求完成;
- Suspense 目前仍属于需要关注版本边界的高级功能。
十三、生命周期和 Scheduler
生命周期 hook 的调用时机与 Scheduler 有关,但不能简单说“生命周期函数都是异步的”:
beforeMount / beforeUpdate / beforeUnmount:对应 render/unmount job 中同步执行
mounted / updated / unmounted:通常排入 post-render/post-flush 队列
watch flush:'pre':默认主队列 PRE job
watch flush:'post':DOM 更新后的 post 队列
watch flush:'sync':响应式变更时同步执行
Vue 3.5 的 scheduler 使用 job flags 和 id 排序,不应直接复制 Vue 3.0 文章中的 pendingPreFlushCbs、queuePreFlushCb 旧源码。要等待本轮 DOM 更新,应使用 nextTick()。
总结
- Options API 和 Composition API 都可以使用 Vue 3 生命周期。
- Composition API 没有
onBeforeCreate/onCreated,创建逻辑放入setup()。 - 正确 API 名称是
onMounted、onBeforeUnmount、onUnmounted。 - mounted/updated/unmounted 的调度时机通常是 post-render,但普通 hook Promise 不会被 Vue 等待。
- KeepAlive 使用 activated/deactivated;Suspense 和异步组件会改变普通父子顺序的简单假设。
- SSR 使用
onServerPrefetch,普通 DOM 生命周期不在服务端执行。 - 调试 hook 只用于开发环境;错误捕获沿父链向上,
return false才停止后续传播。
官方参考
- Vue 生命周期指南
- Composition API 生命周期
- Options API 生命周期
- Vue Watchers
- nextTick
- KeepAlive
- Suspense
- SSR 生命周期
- Vue 3.5.39 Scheduler 源码
原文出处
原文地址:A Complete Guide to Vue Lifecycle Hooks in Vue 3 – LearnVue
作者:旭旭乐是只小菜鸡
原文链接:https://juejin.cn/post/6912632681813508109
来源:稀土掘金。著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。