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

显示模式

登录
ARCHIVE DOCUMENTVUE

Vue 3 生命周期钩子(Hooks)完整指南

所属馆藏
Vue
文件格式
Markdown
原始路径
Vue/32-Vue3生命周期钩子(hooks)完整指南
本文目录16 个章节
  1. 一、生命周期总览
  2. 二、在 Options API 中使用
  3. 三、在 Composition API 中使用
  4. 四、Creation:组件创建阶段
  5. 五、Mounting:访问 DOM
  6. 六、Updates:响应式更新阶段
  7. 七、Unmounting:卸载和清理
  8. 八、KeepAlive:激活和停用
  9. 九、Debug Hooks
  10. 十、错误捕获
  11. 十一、SSR 与 onServerPrefetch
  12. 十二、Suspense 和生命周期边界
  13. 十三、生命周期和 Scheduler
  14. 总结
  15. 官方参考
  16. 原文出处

Vue 3 生命周期钩子(Hooks)完整指南

原文来自 2020 年的 Vue 3 早期文章。本文保留原文从 Options API 迁移到 Composition API、按创建/挂载/更新/卸载/KeepAlive 介绍 hook 的结构,并补充 Vue 3.5 的 onServerPrefetchonWatcherCleanup、Suspense、调度时机和错误边界。

一、生命周期总览

每个组件实例都会经历创建、挂载、更新和卸载等阶段。常用 hook 可以按下表理解:

阶段/能力Options APIComposition API
创建beforeCreatecreatedsetup() / <script setup>
挂载beforeMountmountedonBeforeMountonMounted
更新beforeUpdateupdatedonBeforeUpdateonUpdated
卸载beforeUnmountunmountedonBeforeUnmountonUnmounted
KeepAliveactivateddeactivatedonActivatedonDeactivated
错误errorCapturedonErrorCaptured
调试renderTrackedrenderTriggeredonRenderTrackedonRenderTriggered
SSRserverPrefetchonServerPrefetch

“4 个阶段、8 个主 hook”只是帮助入门的简化分类,不是 Vue 官方固定的生命周期数量。setup() 承担 Composition API 的创建阶段逻辑,Composition API 没有 onBeforeCreateonCreated

原文的生命周期示意图已下载到本地:

Vue 3 生命周期示意图(本地化)

二、在 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 还可以使用 serverPrefetchrenderTrackedrenderTriggered 和 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:组件创建阶段

beforeCreatecreated

在 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 动图已使用当前来源站可下载的同义资源本地化:

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 文章中的 pendingPreFlushCbsqueuePreFlushCb 旧源码。要等待本轮 DOM 更新,应使用 nextTick()

总结

  • Options API 和 Composition API 都可以使用 Vue 3 生命周期。
  • Composition API 没有 onBeforeCreate/onCreated,创建逻辑放入 setup()
  • 正确 API 名称是 onMountedonBeforeUnmountonUnmounted
  • mounted/updated/unmounted 的调度时机通常是 post-render,但普通 hook Promise 不会被 Vue 等待。
  • KeepAlive 使用 activated/deactivated;Suspense 和异步组件会改变普通父子顺序的简单假设。
  • SSR 使用 onServerPrefetch,普通 DOM 生命周期不在服务端执行。
  • 调试 hook 只用于开发环境;错误捕获沿父链向上,return false 才停止后续传播。

官方参考

原文出处

原文地址:A Complete Guide to Vue Lifecycle Hooks in Vue 3 – LearnVue

作者:旭旭乐是只小菜鸡

原文链接:https://juejin.cn/post/6912632681813508109

来源:稀土掘金。著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

457 DOCUMENTS · 10 COLLECTIONS
ARCHIVE SEARCH457 篇文章

SEARCH GUIDE

输入关键词开始搜索

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

按分类浏览

10 COLLECTIONS