news 2026/6/28 1:50:11

05-生命周期与模板引用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
05-生命周期与模板引用

生命周期与模板引用

深入理解 Vue3 组件从创建到销毁的完整生命周期流程,掌握模板引用与组件引用的新用法。

一、前言

生命周期是 Vue 组件的核心概念之一,它描述了组件从创建、挂载、更新到卸载的完整过程。Vue3 在保留 Vue2 生命周期思想的基础上,引入了 Composition API,使得生命周期钩子的使用方式发生了显著变化。

本文将系统讲解 Vue3 的生命周期钩子、与 Vue2 的对比、模板引用的新用法,以及组件挂载的完整流程。

二、Vue3 生命周期钩子概览

2.1 生命周期钩子列表

Vue3 提供了以下生命周期钩子,需要在setup函数中显式导入使用:

生命周期钩子触发时机Vue2 对应钩子
onBeforeMount组件挂载到 DOM 之前beforeMount
onMounted组件挂载到 DOM 之后mounted
onBeforeUpdate组件更新之前beforeUpdate
onUpdated组件更新之后updated
onBeforeUnmount组件卸载之前beforeDestroy
onUnmounted组件卸载之后destroyed
onErrorCaptured捕获后代组件错误时errorCaptured
onRenderTracked调试:追踪响应式依赖
onRenderTriggered调试:触发重新渲染时
onActivatedkeep-alive缓存的组件激活时activated
onDeactivatedkeep-alive缓存的组件停用时deactivated
onServerPrefetch组件在服务器端渲染前serverPrefetch

2.2 基本使用示例

<script setup> import { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue' const count = ref(0) // 挂载前:DOM 尚未创建,此时无法访问模板引用 onBeforeMount(() => { console.log('onBeforeMount: 组件即将挂载') }) // 挂载后:可以安全地访问 DOM 和模板引用 onMounted(() => { console.log('onMounted: 组件已挂载') }) // 更新前:可以获取更新前的 DOM 状态 onBeforeUpdate(() => { console.log('onBeforeUpdate: 组件即将更新') }) // 更新后:DOM 已更新完成 onUpdated(() => { console.log('onUpdated: 组件已更新') }) // 卸载前:组件仍完全可用 onBeforeUnmount(() => { console.log('onBeforeUnmount: 组件即将卸载') }) // 卸载后:清理副作用(定时器、事件监听等) onUnmounted(() => { console.log('onUnmounted: 组件已卸载') }) </script> <template> <div> <p>计数: {{ count }}</p> <button @click="count++">增加</button> </div> </template>

三、生命周期完整流程

3.1 生命周期流程图

创建组件实例

初始化响应式数据

编译模板

onBeforeMount
挂载前

创建并插入 DOM

onMounted
挂载完成

数据变化?

组件卸载?

onBeforeUpdate
更新前

重新渲染 DOM

onUpdated
更新完成

onBeforeUnmount
卸载前

移除 DOM

onUnmounted
卸载完成

垃圾回收

3.2 组件挂载流程详解

Vue3 的组件挂载流程可以分为以下几个阶段:

  1. 创建阶段:调用setup()函数,初始化响应式数据、计算属性和方法
  2. 编译阶段:将模板编译为渲染函数(开发环境下由 Vue 编译器完成)
  3. 挂载前阶段:执行onBeforeMount,此时模板已编译但尚未插入 DOM
  4. 挂载阶段:执行渲染函数,创建虚拟 DOM 并 diff,生成真实 DOM 插入页面
  5. 挂载完成阶段:执行onMounted,此时可以安全访问 DOM 元素
<script setup> import { ref, onBeforeMount, onMounted } from 'vue' const message = ref('Hello Vue3') const containerRef = ref(null) // 挂载前:template 已编译,但 DOM 还未生成 onBeforeMount(() => { // 此时 containerRef.value 为 null console.log('before mount:', containerRef.value) // null }) // 挂载后:DOM 已生成,可以安全操作 onMounted(() => { // containerRef.value 指向真实 DOM 元素 console.log('mounted:', containerRef.value) // <div>Hello Vue3</div> containerRef.value.style.color = 'blue' }) </script> <template> <div ref="containerRef">{{ message }}</div> </template>

四、Vue2 vs Vue3 生命周期对比

4.1 选项式 API vs 组合式 API

特性Vue2 Options APIVue3 Composition API
定义方式options对象中声明setup()中导入调用
代码组织按选项类型分散按逻辑功能聚合
this指向指向组件实例this,通过闭包访问
复用逻辑Mixins(命名冲突、来源不明)Composables(清晰、灵活)
类型推断较差优秀的 TypeScript 支持

4.2 代码对比示例

Vue2 Options API:

exportdefault{data(){return{count:0,timer:null}},mounted(){this.timer=setInterval(()=>{this.count++},1000)},beforeDestroy(){clearInterval(this.timer)}}

Vue3 Composition API:

<script setup> import { ref, onMounted, onUnmounted } from 'vue' const count = ref(0) let timer = null onMounted(() => { timer = setInterval(() => { count.value++ }, 1000) }) onUnmounted(() => { clearInterval(timer) }) </script>

五、模板引用

5.1 基本用法

模板引用用于直接访问 DOM 元素或组件实例。在 Vue3 中,配合ref函数使用:

<script setup> import { ref, onMounted } from 'vue' // 声明模板引用 const inputRef = ref(null) const divRef = ref(null) onMounted(() => { // 挂载后自动填充值并聚焦 inputRef.value.value = '自动填充' inputRef.value.focus() // 操作 DOM 样式 divRef.value.style.backgroundColor = '#f0f0f0' }) </script> <template> <div> <!-- 绑定模板引用 --> <input ref="inputRef" type="text" placeholder="请输入" /> <div ref="divRef">这是一个 div 元素</div> </div> </template>

5.2 v-for 中的模板引用

Vue3 中,v-for中的模板引用需要绑定到函数:

<script setup> import { ref, onMounted } from 'vue' const itemRefs = ref([]) const list = ref(['苹果', '香蕉', '橙子']) onMounted(() => { // itemRefs.value 是一个 DOM 元素数组 itemRefs.value.forEach((el, index) => { console.log(`第 ${index} 项:`, el.textContent) }) }) </script> <template> <ul> <li v-for="(item, index) in list" :key="index" :ref="(el) => { if (el) itemRefs[index] = el }" > {{ item }} </li> </ul> </template>

5.3 组件引用

通过模板引用可以访问子组件的属性和方法:

<!-- ChildComponent.vue --> <script setup> import { ref } from 'vue' const count = ref(0) const increment = () => { count.value++ } const reset = () => { count.value = 0 } // 显式暴露给父组件 defineExpose({ count, increment, reset }) </script> <template> <div>子组件计数: {{ count }}</div> </template>
<!-- ParentComponent.vue --> <script setup> import { ref, onMounted } from 'vue' import ChildComponent from './ChildComponent.vue' const childRef = ref(null) const handleAdd = () => { // 调用子组件暴露的方法 childRef.value.increment() } const handleReset = () => { childRef.value.reset() console.log('当前计数:', childRef.value.count) } </script> <template> <div> <ChildComponent ref="childRef" /> <button @click="handleAdd">子组件 +1</button> <button @click="handleReset">子组件重置</button> </div> </template>

5.4 $parent 与 $children 的变化

Vue3 中不再推荐使用$parent$children

特性Vue2Vue3
$parent直接访问父组件实例仍可用但不推荐,破坏封装
$children直接访问子组件数组已移除
推荐方案props/emitsprovide/inject
<script setup> // Vue3 中应优先使用 props 和 emits 进行父子通信 import { defineProps, defineEmits } from 'vue' const props = defineProps({ message: String }) const emit = defineEmits(['update']) const handleClick = () => { emit('update', '新消息') } </script>

六、setup 中使用生命周期的注意事项

6.1 执行顺序

setup中,生命周期钩子的注册顺序就是执行顺序:

<script setup> import { onMounted } from 'vue' // 多个 onMounted 会按注册顺序执行 onMounted(() => { console.log('第一个 onMounted') }) onMounted(() => { console.log('第二个 onMounted') }) </script>

6.2 异步 setup

setup可以是异步函数,但需要注意:

<script setup> import { onMounted } from 'vue' // 异步 setup const data = await fetchData() // 顶层 await // 生命周期钩子仍然有效 onMounted(() => { console.log('挂载完成,数据:', data) }) </script>

6.3 条件注册

生命周期钩子可以在条件语句中注册:

<script setup> import { ref, onMounted } from 'vue' const needTimer = ref(true) if (needTimer.value) { onMounted(() => { // 只在 needTimer 为 true 时注册 console.log('定时器模式已启动') }) } </script>

七、调试钩子:onRenderTracked 与 onRenderTriggered

这两个钩子仅用于调试,帮助追踪响应式依赖:

<script setup> import { ref, onRenderTracked, onRenderTriggered } from 'vue' const count = ref(0) const name = ref('Vue3') // 追踪响应式依赖的收集 onRenderTracked((event) => { console.log('追踪到依赖:', event) // event.target: 被追踪的响应式对象 // event.type: 追踪类型 (get / has / iterate) // event.key: 被访问的属性键 }) // 追踪重新渲染的触发 onRenderTriggered((event) => { console.log('触发重新渲染:', event) // event.target: 触发更新的响应式对象 // event.type: 触发类型 (set / add / delete / clear) // event.key: 被修改的属性键 }) </script> <template> <div> <p>{{ name }} 计数: {{ count }}</p> <button @click="count++">增加</button> <button @click="name = 'Vue3 Pro'">改名</button> </div> </template>

八、常见问题

Q1: 为什么 onMounted 中访问 ref 有时为 null?

通常是因为组件尚未挂载完成,或者使用了v-if条件渲染。确保在onMounted中访问,或使用nextTick

import{ref,onMounted,nextTick}from'vue'constelRef=ref(null)onMounted(async()=>{awaitnextTick()// 确保 DOM 已更新console.log(elRef.value)})

Q2: Vue3 中 destroyed 钩子去哪了?

Vue3 将beforeDestroy重命名为onBeforeUnmountdestroyed重命名为onUnmounted,命名更准确反映实际行为(卸载而非销毁)。

Q3: 如何在组合式函数中注册生命周期钩子?

组合式函数中可以直接使用生命周期钩子,它们会与调用组件的生命周期同步:

// useAutoIncrement.jsimport{ref,onMounted,onUnmounted}from'vue'exportfunctionuseAutoIncrement(initial=0,interval=1000){constcount=ref(initial)lettimer=nullonMounted(()=>{timer=setInterval(()=>{count.value++},interval)})onUnmounted(()=>{clearInterval(timer)})return{count}}

Q4: 父子组件生命周期执行顺序是什么?

挂载阶段:父onBeforeMount→ 子onBeforeMount→ 子onMounted→ 父onMounted
卸载阶段:父onBeforeUnmount→ 子onBeforeUnmount→ 子onUnmounted→ 父onUnmounted

九、总结

Vue3 的生命周期系统保留了 Vue2 的核心概念,同时通过 Composition API 提供了更灵活的使用方式:

  1. 钩子命名更语义化destroyedunmounted,更准确表达组件状态
  2. 显式导入:所有钩子需要从vue导入,Tree-shaking 更友好
  3. 逻辑聚合:相关逻辑可以放在一起,不再分散在不同选项中
  4. 模板引用更简洁:配合ref函数使用,类型推断更友好
  5. 组件引用需显式暴露:通过defineExpose控制暴露接口,封装性更好

十、思考题/练习

  1. 代码练习:编写一个自定义组合式函数useCountdown,实现倒计时功能,在组件卸载时自动清理定时器。

  2. 对比分析:将一段 Vue2 的 Options API 代码(包含 data、mounted、beforeDestroy)改写为 Vue3 的 Composition API 版本。

  3. 生命周期顺序:画出包含三层嵌套组件(祖父 → 父 → 子)的挂载和卸载生命周期执行顺序图。

  4. 实践挑战:实现一个useIntersectionObserver组合式函数,使用模板引用监听元素是否进入视口。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/28 1:46:02

你被身份验证折磨过吗?

每次新起一个 FastAPI 项目&#xff0c;聊到登录注册、用户角色、Token 刷新这些话题&#xff0c;群里总是一片哀嚎。明明 FastAPI 官方文档把请求体、依赖注入讲得明明白白&#xff0c;可一到身份验证&#xff0c;瞬间变成大型面向复制粘贴编程现场。那一刻我就知道&#xff0…

作者头像 李华
网站建设 2026/6/28 1:42:09

数据科学与大数据技术专业的专业发展分析

随着数字化转型在各行各业深入推进&#xff0c;“数据科学与大数据技术”专业成为高等教育领域的热门选择。本文旨在从学科内涵、学生适配性、阶段性发展规划及行业资质认证等维度&#xff0c;对该专业进行客观分析&#xff0c;以期为考生及家长提供参考。一、专业核心内涵与知…

作者头像 李华
网站建设 2026/6/28 1:38:04

【毕业设计】基于 SpringBoot+Vue 的企业员工岗前培训学习平台的设计与实现 基于 SpringBoot+Vue 的职工技能知识教育培训系统(源码+文档+远程调试,全bao定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/6/28 1:17:55

深圳人工智能培训机构有哪些?深度解析行业现状与择校要点

打开各大招聘平台&#xff0c;输入“人工智能”三个字&#xff0c;映入眼帘的薪资数字往往让人眼前一亮。深圳作为粤港澳大湾区的核心城市&#xff0c;聚集了华为、腾讯、平安科技、大疆等科技巨头&#xff0c;人工智能相关岗位需求量长期位居全国前列。从招聘数据来看&#xf…

作者头像 李华
网站建设 2026/6/28 1:17:29

Print.js 与 web-print-pdf 对比

Print.js&#xff08;npm 包名 print-js&#xff09;是前端里很常见的「把某段 HTML / PDF / 图片交给浏览器打印」工具。 web-print-pdf 则是「通过本地客户端静默出纸」方案。 两者名字里都有 print&#xff0c;但解决的问题层级不同。本文帮助你在 Print.js 够用 与 必须上…

作者头像 李华