技术知识文章集合TECHNICAL ARCHIVE · 457 DOCUMENTS

显示模式

登录
ARCHIVE DOCUMENTVUE

详解 Vue 的生命周期

所属馆藏
Vue
文件格式
Markdown
原始路径
Vue/101-详解Vue的生命周期
本文目录9 个章节
  1. 一、什么是组件生命周期
  2. 二、生命周期执行顺序
  3. 三、用 Vue 2 代码观察生命周期
  4. 四、各阶段适合做什么
  5. 五、父子组件生命周期顺序
  6. 六、请求放在哪个生命周期更合适
  7. 七、其他常用生命周期能力
  8. 总结
  9. 参考资料

详解 Vue 的生命周期

原文主要介绍 Vue 2 Options API 的创建、挂载、更新、销毁阶段,并讨论父子组件顺序和请求放在哪个钩子。本文保留这条学习路线,修复生命周期顺序、beforeMount 作用、请求时机和 SSR 相关表述,并补充 Vue 3.5 Composition API 的对应钩子。

一、什么是组件生命周期

组件生命周期描述的是一个组件从创建、初始化响应式状态、挂载到 DOM、响应式更新,直到卸载的过程。

可以把它抽象为:

创建与初始化 → 挂载 → 更新 → 卸载

Vue 2 和 Vue 3 都有生命周期概念,但 API 名称有差异:

阶段Vue 2 Options APIVue 3 Options API / Composition API
创建前/创建后beforeCreate / createdOptions API 仍有;setup() 承担组合式初始化
挂载beforeMount / mountedbeforeMount / mounted,或 onBeforeMount / onMounted
更新beforeUpdate / updatedbeforeUpdate / updated,或 onBeforeUpdate / onUpdated
卸载beforeDestroy / destroyedbeforeUnmount / unmounted,或 onBeforeUnmount / onUnmounted

Vue 3 将 destroy 命名为 unmount,因为组件是从应用树中卸载,而不是简单地“销毁一个对象”。Vue 3 已移除 beforeDestroydestroyed 这两个旧名称。

Vue 生命周期概览图(原文图片已本地化)

二、生命周期执行顺序

一个同步组件的典型顺序如下。

2.1 Vue 2 Options API 创建和挂载

父 beforeCreate
父 created
父 beforeMount
  子 beforeCreate
  子 created
  子 beforeMount
  子 mounted
父 mounted

如果只列出原文重点的钩子,就是:

父 beforeMount
  → 子 created
  → 子 mounted
→ 父 mounted

但完整顺序还包括子组件的 beforeCreate、父组件的 beforeCreate/created 和子组件的 beforeMount,不能把简化列表当成完整生命周期。

2.2 Vue 3

Vue 3 的 Options API 顺序与上述总体关系相近;Composition API 则在 setup() 中注册 onMounted 等钩子:

<script setup>
import { onBeforeMount, onMounted } from 'vue'

onBeforeMount(() => {
  console.log('组件即将挂载')
})

onMounted(() => {
  console.log('组件已经挂载')
})
</script>

Composition API 生命周期钩子必须在 setup() 执行期间同步注册。不能等异步请求完成后才调用 onMounted,否则可能无法绑定到当前组件实例。

Vue 生命周期执行顺序示意图(原文图片已本地化)

三、用 Vue 2 代码观察生命周期

原文的控制台示例被网页抓取成一行,下面整理为可读的 Vue 2 Options API 版本:

new Vue({
  el: '#app',
  data() {
    return {
      message: 'hello',
    }
  },

  beforeCreate() {
    console.log('beforeCreate', {
      el: this.$el,
      data: this.$data,
      message: this.message,
    })
  },

  created() {
    console.log('created', {
      el: this.$el, // 通常还没有挂载完成
      data: this.$data,
      message: this.message,
    })
  },

  beforeMount() {
    console.log('beforeMount', this.$el)
  },

  mounted() {
    console.log('mounted', this.$el, this.$refs)
  },

  beforeUpdate() {
    console.log('beforeUpdate:即将根据新状态更新 DOM')
  },

  updated() {
    console.log('updated:组件 DOM 已完成本轮更新')
  },

  beforeDestroy() {
    console.log('beforeDestroy:清理仍可访问的资源')
  },

  destroyed() {
    console.log('destroyed:实例已从 Vue 管理树中移除')
  },
})

注意:this.$elcreated/beforeMount 阶段可能是占位元素或尚未指向最终 DOM,具体还和是否提供 el、模板编译方式有关;不要只凭某一个控制台截图断言所有场景都严格为 undefined

Vue 2 生命周期控制台输出示例(原文图片已本地化)

四、各阶段适合做什么

4.1 beforeCreate / created

beforeCreate 很早执行,此时组件实例的 data、computed、methods 等初始化尚未完成,通常不适合执行业务逻辑。

created 执行时,Vue 2 的 data observation、computed、methods 和 watch 等已经完成,可以访问数据并发起不依赖 DOM 的请求:

export default {
  data() {
    return {
      list: [],
    }
  },

  async created() {
    this.list = await fetchList()
  },
}

Vue 3 <script setup> 中通常直接在顶层调用 composable 或执行初始化逻辑:

<script setup>
import { ref } from 'vue'

const list = ref([])
const loading = ref(true)

fetchList()
  .then(data => {
    list.value = data
  })
  .finally(() => {
    loading.value = false
  })
</script>

4.2 beforeMount / onBeforeMount

它们在组件即将进行首次 DOM 挂载时调用。此时组件已经完成响应式状态和 render 准备,但不应把它当成“获取 DOM 的安全时机”。需要 DOM 时,优先使用 mounted/onMounted

beforeMount 也不等于“此时一定把模板编译成 render”。在构建项目中模板通常早已由编译器转换;运行时编译版本才可能在运行阶段编译模板。

4.3 mounted / onMounted

组件挂载后可以访问组件自己的 DOM 和 $refs

<script setup>
import { onMounted, ref } from 'vue'

const input = ref(null)

onMounted(() => {
  input.value?.focus()
})
</script>

<template>
  <input ref="input" />
</template>

onMounted 表示同步子组件已经挂载,且组件自己的 DOM 已插入容器;异步组件、Suspense 或后续异步渲染需要根据具体边界处理,不能简单理解为整棵应用树都已完成。

适合做:

  • 读取尺寸、位置等 DOM 信息;
  • 初始化只在浏览器存在的第三方 DOM 库;
  • 注册需要 DOM 目标的事件;
  • 执行首屏动画。

如果任务不依赖 DOM,不能因为“mounted 更晚”就认为它一定比 created/setup 发请求更好。

Vue 组件挂载阶段示例(原文图片已本地化)

4.4 beforeUpdate / onBeforeUpdate

响应式状态导致组件重新渲染、但 DOM 尚未应用本轮更新时调用。此时访问到的通常是旧 DOM 状态。

import { onBeforeUpdate } from 'vue'

onBeforeUpdate(() => {
  // 读取本轮更新前的 DOM 状态
})

它适合少量需要在 DOM 更新前记录的信息。不要在这里无条件修改响应式状态,否则可能制造额外更新或循环。

4.5 updated / onUpdated

组件 DOM 完成本轮更新后调用:

import { onUpdated, nextTick } from 'vue'

onUpdated(() => {
  // 可以读取更新后的 DOM
})

async function updateAndMeasure() {
  state.value++
  await nextTick()
  // 只需要等待当前更新完成时,nextTick 往往更直接
}

updated 不应该用来无条件修改依赖自身的状态,否则可能形成更新循环。对于一次操作后的 DOM 读取,优先在操作函数中 await nextTick(),而不是把所有逻辑塞进全局 updated

4.6 Vue 2 beforeDestroy / destroyed

Vue 2 中,beforeDestroy 适合清理定时器、全局事件、WebSocket、第三方实例和手动注册的监听器:

export default {
  mounted() {
    window.addEventListener('resize', this.handleResize)
    this.timer = window.setInterval(this.refresh, 10_000)
  },

  beforeDestroy() {
    window.removeEventListener('resize', this.handleResize)
    clearInterval(this.timer)
    this.socket?.close()
    this.chart?.dispose()
  },
}

destroyed 执行时,实例已经从 Vue 的父子关系和响应式观察中移除。某些普通 JavaScript 属性仍可能存在,但不要把它当作 DOM 或响应式资源仍然有效的保证。

4.7 Vue 3 beforeUnmount / onUnmounted

Vue 3 对应写法:

<script setup>
import { onMounted, onUnmounted } from 'vue'

function handleResize() {
  // ...
}

onMounted(() => {
  window.addEventListener('resize', handleResize)
})

onUnmounted(() => {
  window.removeEventListener('resize', handleResize)
})
</script>

onUnmounted 适合清理组件创建的副作用。Vue 3.5 还可以使用 onWatcherCleanup 清理 watcher 中过期的请求,但它属于 watcher 清理 API,不是组件生命周期钩子的替代品。

Vue 3 卸载和清理副作用示意图(原文图片已本地化)

五、父子组件生命周期顺序

对于同步父子组件,常见的顺序如下。

挂载阶段

父 beforeCreate
父 created
父 beforeMount
  子 beforeCreate
  子 created
  子 beforeMount
  子 mounted
父 mounted

父组件的 mounted 在其同步子组件都挂载后执行。原文“父组件挂载前,子组件已经挂载完成”的说法不准确:父 beforeMount 会先于子组件挂载,但父 mounted 会晚于同步子组件 mounted

更新阶段

当父组件更新导致子树也更新时,常见顺序为:

父 beforeUpdate
  子 beforeUpdate
  子 updated
父 updated

具体顺序还会受到更新来源、组件是否真的有变化、scheduler 批处理和异步边界影响;不要用一个子组件示例推断所有更新任务的全局顺序。

卸载阶段

Vue 2:

父 beforeDestroy
  子 beforeDestroy
  子 destroyed
父 destroyed

Vue 3 对应为:

父 beforeUnmount
  子 beforeUnmount
  子 unmounted
父 unmounted

如果组件由 <KeepAlive> 缓存,切换时通常触发 activated/deactivated(或 onActivated/onDeactivated),而不是立即卸载;这与真正从组件树移除的 unmount 不同。

父子组件生命周期顺序示意图(原文图片已本地化)

可以用一个子组件日志观察局部顺序:

export default {
  created() {
    console.log('list created')
  },
  mounted() {
    console.log('list mounted')
  },
  beforeUpdate() {
    console.log('list before update')
  },
  updated() {
    console.log('list updated')
  },
  beforeDestroy() {
    console.log('list beforeDestroy')
  },
  destroyed() {
    console.log('list destroyed')
  },
}

Vue 3 Composition API 则写成 onMountedonUpdatedonUnmounted

六、请求放在哪个生命周期更合适

“请求一定放在 mountedcreated 好”并不是通用结论。选择标准是请求是否依赖 DOM,以及项目是否需要 SSR:

场景合适位置
Vue 2,数据请求不依赖 DOMcreated 常见,也可以在路由进入守卫或其他服务层请求
Vue 2,请求完成后必须操作 DOM请求可在 created,DOM 操作放到 mounted/$nextTick
Vue 3,普通组件初始化setup() 顶层、composable 或 onBeforeMount 前的业务逻辑
Vue 3,仅浏览器可用的 APIonMounted
Nuxt 4,需要 SSR/预取/缓存useFetchuseAsyncData 或服务端数据加载方案
监听参数变化重新请求watch/watchEffect,并清理过期请求

请求本身通常不需要等待 DOM。把请求延后到 mounted 可能反而推迟数据加载;只有请求依赖浏览器 API、组件已挂载状态或第三方 DOM 实例时,才应该放在 onMounted

Vue 3 SSR 中,onMountedonUpdatedonUnmounted 等仅客户端钩子不会在服务端执行;需要服务端预取时,应使用 onServerPrefetch 或 Nuxt 的数据获取 API。setup() 和 Options API 的 created 可能在服务端执行,因此其中不要直接访问 windowdocument

生命周期与异步请求时机示意图(原文图片已本地化)

七、其他常用生命周期能力

7.1 <KeepAlive>

缓存组件切换时使用:

import { onActivated, onDeactivated } from 'vue'

onActivated(() => {
  // 组件从缓存中重新激活
})

onDeactivated(() => {
  // 组件进入缓存或被卸载
})

这两个钩子适合暂停轮询、重新获取可见数据或管理缓存页面资源。

7.2 错误捕获

Options API 可以使用 errorCaptured,Composition API 可以使用 onErrorCaptured 捕获后代组件的错误,并决定是否继续向上传播。它不是全局异常监控的完整替代方案,生产环境还应接入统一错误上报。

7.3 调试响应式依赖

Vue 3 开发环境提供 onRenderTrackedonRenderTriggered,帮助定位组件为什么收集或触发了某个响应式依赖。它们适合调试,不应作为业务逻辑依赖。

生命周期调试与组件状态观察示例(原文图片已本地化)

总结

  1. 生命周期描述组件从创建、挂载、更新到卸载的过程;
  2. Vue 2 使用 beforeDestroy/destroyed,Vue 3 使用 beforeUnmount/unmounted
  3. Vue 3 Composition API 在 setup() 中注册 onMounted 等钩子;
  4. 父子组件顺序通常是父 beforeMount、子完整挂载、父 mounted;更新和卸载也遵循由外到内再由内到外的总体关系;
  5. created/setup 适合做不依赖 DOM 的数据初始化,mounted/onMounted 适合 DOM 相关操作;
  6. 请求不必为了“更晚”而放到 mounted,SSR 和 Nuxt 还要使用服务端可执行的数据获取方案;
  7. 组件卸载前应清理定时器、事件、连接和第三方实例;
  8. KeepAlive 切换会触发 activated/deactivated,不等同于真正卸载。

参考资料

原文作者:是乃德也是 Ned。原文关于 Vue 2 生命周期阶段、控制台观察、父子顺序、资源清理和请求时机的主线予以保留;抓取噪声、生命周期顺序遗漏、mounted 一定更适合请求、SSR 表述和 Vue 2/3 API 混淆已修正或补充。

457 DOCUMENTS · 10 COLLECTIONS
ARCHIVE SEARCH457 篇文章

SEARCH GUIDE

输入关键词开始搜索

支持搜索文章标题、所属分类和原始文档路径。

按分类浏览

10 COLLECTIONS