跳到主要内容

Vue 2 / Vue 3 生命周期:从创建到卸载(含执行顺序)

生命周期(Lifecycle)说白了就是:组件从“出生”到“死亡”这一路上,会在固定的时间点给你一些“插口”(钩子)让你写代码

掌握生命周期最大的价值在于两点:

  1. 知道代码该放哪:什么时候能拿到数据?什么时候才能安全操作 DOM?什么时候必须做清理?
  2. 排查问题更快:比如“为什么 mounted 里拿不到最新 DOM”“为什么 keep-alive 切换没触发销毁”等。

下面我们把 Vue 2 和 Vue 3 的生命周期按“阶段 + 执行顺序”讲清楚,并补上父子组件的先后顺序。

先记一条主线

挂载(mount)→ 更新(update)→ 卸载(unmount)。生命周期钩子就是围绕这三段展开的。

1. Vue 2 生命周期(Options API)

Vue 2 的常用生命周期钩子如下:

  • 创建阶段beforeCreatecreated
  • 挂载阶段beforeMountmounted
  • 更新阶段beforeUpdateupdated
  • 销毁阶段beforeDestroydestroyed
  • keep-alive 专属activated / deactivated
  • 异常捕获errorCaptured(捕获后代组件错误)

1.1 首次渲染(挂载)执行顺序

顺序:

  1. beforeCreate:此时 data/props 还没初始化,this.xxx 基本拿不到你想要的东西
  2. createddata/props/computed/methods 已准备好,但 DOM 还没挂载
  3. beforeMount:即将把虚拟 DOM 渲染成真实 DOM
  4. mounted:真实 DOM 已挂载,适合做 DOM 读写、三方库初始化
常见坑:created 里操作 DOM

created 时 DOM 还不存在。需要 DOM 的逻辑,请放到 mounted,或配合 this.$nextTick()

1.2 数据更新(重新渲染)执行顺序

当响应式数据变化触发重新渲染时:

  1. beforeUpdate:DOM 还没更新,你在这里读到的往往还是旧 DOM
  2. updated:DOM 已更新完成,可以在这里读取更新后的 DOM(但别在这里再无脑改状态,容易循环更新)

1.3 销毁(卸载)执行顺序

当组件即将从页面移除时:

  1. beforeDestroy:适合做清理(取消订阅、清除定时器、解绑事件等)
  2. destroyed:组件实例与响应式连接已断开,子组件也都被销毁

1.4 keep-alive:activated / deactivated 的顺序

<keep-alive> 缓存的组件,切换离开/回来时不会走“销毁”,而是:

  • 首次进入:完整挂载流程 + activated
  • 切换离开deactivated
  • 切换回来activated
  • 真正移除缓存(例如切换到不再命中、或缓存被淘汰):才会走 beforeDestroy / destroyed
建议

需要“每次页面切回来刷新数据”的场景,优先考虑 activated,而不是 mounted

2. Vue 3 生命周期(Options API + Composition API)

Vue 3 既支持 Options API(写法类似 Vue 2),也支持 Composition API(setup/<script setup>)。

2.1 命名变化:destroy → unmount

Vue 3 把“销毁(destroy)”这组名字改成了“卸载(unmount)”:

  • beforeDestroybeforeUnmount
  • destroyedunmounted

2.2 setup 的位置(非常重要)

在 Composition API 中:

  • setup() 在组件创建之初执行,你可以把它理解为“更早的 created”
  • setup() 里拿不到 this(因为还没有组件实例的 this 语义),逻辑都用局部变量、ref/reactive 来组织
  • onMounted/onUpdated/onUnmounted... 这类钩子,都需要在 setup() 里注册
结论

Vue 3 Composition API 下,没有“onCreated / onBeforeCreate”,因为 setup() 就是那一段。

2.3 Vue 2 / Vue 3 钩子对照表(背这一张基本够用)

Vue 2(Options)Vue 3(Options)Vue 3(Composition)
beforeCreatebeforeCreate(兼容)setup() 里的同步代码替代
createdcreated(兼容)setup() 里的同步代码替代
beforeMountbeforeMountonBeforeMount
mountedmountedonMounted
beforeUpdatebeforeUpdateonBeforeUpdate
updatedupdatedonUpdated
beforeDestroybeforeUnmountonBeforeUnmount
destroyedunmountedonUnmounted
activatedactivatedonActivated
deactivateddeactivatedonDeactivated
errorCapturederrorCapturedonErrorCaptured
SSR 提醒

onMounted/onUpdated 只会在浏览器端执行;SSR 渲染阶段只会跑 setup() 以及与渲染相关的逻辑。

2.4 Vue 3(Composition API)执行顺序速记

把 Vue 3 的 Composition API 生命周期按阶段记最省事:

  • 首次挂载setup()onBeforeMountonMounted
  • 更新onBeforeUpdateonUpdated
  • 卸载onBeforeUnmountonUnmounted
  • keep-aliveonActivated / onDeactivated
混用 Options API + setup 的顺序

如果你同时写了 setup()beforeCreate/created(不推荐但可能存在),setup() 会更早执行;之后才进入 Options API 的生命周期钩子。

3. 执行顺序:父子组件谁先谁后(面试高频)

生命周期的“单组件顺序”好背,但真正容易栽坑的是 父子组件的先后顺序

下面给出最常见的执行规律(Vue 2 / Vue 3 都基本一致):

3.1 挂载:先父后子,mounted 反过来

典型顺序:

  1. 父:beforeCreatecreatedbeforeMount
  2. 子:beforeCreatecreatedbeforeMountmounted
  3. 父:mounted

3.2 更新:先父后子,updated 反过来

当父组件的数据变化导致父子都需要重新渲染时:

  1. 父:beforeUpdate
  2. 子:beforeUpdate
  3. 子:updated
  4. 父:updated
小补充

如果“父更新不影响子”(比如子组件不依赖变化的 props、或子组件被 v-once 固定),子组件可能不会触发更新钩子。

3.3 卸载:先父后子,unmounted 反过来

卸载时的规律也类似:

  1. 父:beforeUnmount(Vue 2 是 beforeDestroy
  2. 子:beforeUnmount / beforeDestroy
  3. 子:unmounted / destroyed
  4. 父:unmounted / destroyed

4. 用法指南:逻辑到底放哪个钩子?

把“放哪”这件事说清楚,你生命周期就算真正学会了。

4.1 请求数据:created / setup 更常用

  • Vue 2:通常放 created(拿得到 data/props,且越早越好)
  • Vue 3:通常放 setup()(或在 setup 里触发请求)
注意

如果请求依赖 DOM 尺寸(比如要读容器宽高再请求),那必须放到 mounted/onMounted 后。

4.2 操作 DOM:mounted / onMounted + nextTick

需要拿元素、读尺寸、初始化图表库(ECharts)等:

  • Vue 2:mounted,必要时 this.$nextTick(() => {})
  • Vue 3:onMounted,必要时 nextTick(() => {})

4.3 清理副作用:beforeUnmount / onBeforeUnmount

下面这些如果不清理,最容易内存泄漏:

  • setInterval/setTimeout
  • window.addEventListener
  • WebSocket / EventSource
  • 自己写的订阅(pub/sub)

Vue 2 推荐放 beforeDestroy,Vue 3 推荐放 onBeforeUnmountonUnmounted

5. 例子:打印完整执行顺序(Vue 2 / Vue 3)

5.1 Vue 2(父子组件日志)

父组件与子组件各写一套钩子,刷新页面你就能看到顺序:

// Parent.vue(示例)
export default {
name: 'Parent',
beforeCreate() { console.log('P beforeCreate') },
created() { console.log('P created') },
beforeMount() { console.log('P beforeMount') },
mounted() { console.log('P mounted') },
beforeUpdate() { console.log('P beforeUpdate') },
updated() { console.log('P updated') },
beforeDestroy() { console.log('P beforeDestroy') },
destroyed() { console.log('P destroyed') },
}

你会得到类似这样的输出(仅展示挂载阶段):

P beforeCreate
P created
P beforeMount
C beforeCreate
C created
C beforeMount
C mounted
P mounted

5.2 Vue 3(Composition API 日志)

import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue'

export default {
name: 'Demo',
setup() {
console.log('setup')

onBeforeMount(() => console.log('onBeforeMount'))
onMounted(() => console.log('onMounted'))
onBeforeUpdate(() => console.log('onBeforeUpdate'))
onUpdated(() => console.log('onUpdated'))
onBeforeUnmount(() => console.log('onBeforeUnmount'))
onUnmounted(() => console.log('onUnmounted'))
},
}
看到 setup 就别紧张

setup() 是 Vue 3 组织逻辑的入口,它出现得很早;真正“DOM 可用”的时机还是 onMounted

6. 面试高频问答

Q1:Vue 2 和 Vue 3 生命周期最大的区别是什么?

参考回答:

本质流程(挂载/更新/卸载)没变,主要差异是两点:一是 Vue 3 把 beforeDestroy/destroyed 改名为 beforeUnmount/unmounted;二是 Vue 3 引入 Composition API,setup() 在创建之初执行,很多原本写在 beforeCreate/created 的逻辑会前移到 setup(),并通过 onMounted/onUpdated/... 注册后续钩子。


Q2:为什么 created 里拿不到 DOM?应该放在哪?

参考回答:

created 阶段只完成了数据与依赖初始化,还没把虚拟 DOM 渲染成真实 DOM,所以 DOM 不存在或不完整。需要 DOM 的逻辑应该放 mounted(Vue 2)/onMounted(Vue 3),如果还要确保“DOM 更新后再读”,再配合 nextTick


Q3:父子组件挂载时,mounted 谁先执行?

参考回答:

一般是 子组件先 mounted,父组件后 mounted。父组件会先走到 beforeMount,然后开始创建/挂载子组件,等子组件 mounted 完成后,父组件才会进入 mounted


Q4:父子组件更新时,updated 谁先执行?

参考回答:

一般是 子组件先 updated,父组件后 updated。顺序通常是:父 beforeUpdate → 子 beforeUpdate → 子 updated → 父 updated


Q5:Vue 3 里 setup()onMounted() 分别适合做什么?

参考回答:

setup() 适合做“与 DOM 无关的初始化”,比如声明响应式状态、计算属性、方法、发起不依赖 DOM 的请求、注册生命周期钩子等;onMounted() 适合做“需要真实 DOM 的事情”,比如读写元素、初始化三方 UI/图表库、拿尺寸、绑定与 DOM 强相关的监听。


Q6:keep-alive 缓存组件为什么切换路由不触发销毁?那我该在哪刷新数据?

参考回答:

因为 keep-alive 的目标就是“缓存组件实例”,切换离开时不会卸载,只会触发 deactivated,回来触发 activated。需要“每次回来都刷新”的逻辑应该写在 activated/onActivated,而不是 mounted/onMounted


Q7:beforeUpdateupdated 的区别是什么?有什么注意点?

参考回答:

beforeUpdate 时 DOM 还没被 patch,你读到的通常是旧 DOM;updated 时 DOM 已更新完成,适合读取更新后的 DOM。但 updated 里不要再无脑改触发渲染的状态,否则容易出现循环更新或性能问题。


Q8:清理定时器/事件监听应该写在哪个钩子?写 unmounted 行不行?

参考回答:

Vue 2 放 beforeDestroy,Vue 3 放 onBeforeUnmountonUnmounted 都行。实践里更推荐在 “before” 阶段清理(beforeDestroy/onBeforeUnmount),因为此时组件还处在可控状态,清理更稳;但放在 unmounted 也能生效,只要确保一定会执行到(非 keep-alive 缓存切换)。