尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Vue生命周期完全指南:钩子函数、组合式API与实战避坑

Vue生命周期完全指南:钩子函数、组合式API与实战避坑 1. 生命周期到底是什么从一个组件的前世今生说起Vue的生命周期说白了就是一个组件从“被创建”到“被销毁”的完整过程。我当年刚接触Vue的时候看了不少教程都说生命周期是Vue的“核心机制”但翻来覆去就那么一张官方流程图配一段钩子函数的中文翻译看完还是一头雾水。为什么这个东西这么重要因为你写任何Vue组件本质上都是在跟它的生命周期打交道。你什么时候去请求数据什么时候操作DOM什么时候解绑事件什么时候销毁定时器这些问题如果搞不清楚项目上线之后等着你的就是白屏、报错、内存泄漏甚至数据错乱。官方那张生命周期图很多新手看第一眼就蒙了。那一堆箭头指向来指过去跟地铁线路图似的。我换个说法你试试把Vue组件理解成一个“人”生命周期就是从出生到死亡的各个阶段——出生前、诞生、挂载上墙、日常更新、销毁离场。每个阶段Vue都会给你一个“回调时机”这就是生命周期钩子函数。Vue 3.x和Vue 2.x在生命周期上最大的差异是**Composition API组合式API**出现后钩子的使用位置和方式彻底变了。现在新项目基本都直接上Vue 3但我自己带团队时老项目里还有不少2.x的代码在跑两套东西都得会看这也算这个方向的“基本功”了。这篇文章我打算按自己实战的节奏来讲从整体设计思路入手把每个钩子函数的使用场景拆开揉碎再讲几个高频业务场景的完整实操最后把踩过的坑和面试考法都整理出来。你能拿到手的不是“生命周期是什么”而是“生命周期到底怎么用”以及“用的时候有哪些坑我帮你先踩了”。2. 八个钩子函数逐个拆解每个阶段的职责与使用场景2.1 从beforeCreate到created组件的“新生儿”阶段beforeCreate是Vue实例初始化时触发的第一个钩子触发的时机在事件初始化之后、数据响应式和事件监听设置之前。这个阶段有个特点你在里面拿不到data里的数据也拿不到methods里的方法this都还不完整。写代码的时候就会发现这个钩子真正能用上的场景极少。我做过的项目里唯一遇到的一次是在一个老同事的代码里他用beforeCreate做了全局配置的注入那也是在Vue 2的beforeCreate阶段把一些插件级别的配置挂到this上——不过这种写法现在基本被app.config.globalProperties替代了非必要不推荐在这个钩子里做业务操作。created阶段就完全不一样了。组件实例已经完成初始化数据已经变成响应式的methods也绑定上来了。很多人选在这个钩子发起异步请求因为这个时候你访问this.$route拿路由参数、读this.xxx取data里的值完全没问题。2.2 beforeMount与mounted模板编译与DOM挂载的前后分界线beforeMount触发的时机是编译好的模板即将挂载到真实DOM上之前。到了这一步组件甚至还没有完成最终的渲染。这个阶段能访问到实例数据但DOM还没生成如果你试图去document.getElementById找一个组件内部的元素大概率返回undefined。真正在实践里值得反复琢磨的是mounted。这是组件挂载完成、真实DOM已经插入页面之后触发的钩子也是做DOM操作的首选位置。this.$el可以直接拿到根元素子组件的引用ref也可以正常访问了。实际操作中不少人在这里踩了一个细节坑mounted只在“初始挂载”时触发一次不保证你渲染的异步数据已经到位。比如你用v-for渲染一个列表列表数据是mounted里fetch回来的这个阶段你放心大胆地做DOM测量大概率会拿到空值或0。我在做图表类需求时反复被这个问题折磨过后面在组件里加了nextTick配合监听数据变化才彻底解决这个细节放到第四部分“常见问题排查”里展开说。2.3 beforeUpdate与updated数据变化驱动DOM更新的前后时机这两个钩子是我觉得最容易被误用的一组。beforeUpdate在数据变化之后、DOM重新渲染之前触发适合在更新前访问现有的DOM状态比如获取用户滚动位置、记录旧数据等。updated则在虚拟DOM重新渲染并更新真实DOM后触发此时可以执行依赖最新DOM的操作。数据驱动视图是Vue的核心理念数据一变视图跟着变。更新阶段的钩子作用就是让你能在这个变动的过程中“插一脚”。但这里必须强调一个原则不要在updated里再次修改数据。一旦你在updated里改了响应式数据就会触发新一轮的更新再进一次updated再改再进直接死循环页面卡死控制台报错这种错误我见过不少新人写过。2.4 beforeUnmount与unmounted组件销毁阶段的“善后”工作Vue 3里组件销毁阶段的两个钩子是beforeUnmount和unmounted对应Vue 2中的beforeDestroy和destroyed。名称变了职责没变beforeUnmount组件即将销毁、实例功能仍然可用适合做清理前的最后准备工作unmounted组件已经销毁实例被完全卸载一些DOM的引用全部失效我处理过的典型场景是定时器清理和全局事件解绑。比如一个倒计时组件你在mounted里设置了setInterval如果不在销毁阶段clearInterval组件被切走之后计时器还在跑内存泄漏、性能下降就是这么来的。类似的还有window.addEventListener(resize, handler)不监听就要解绑否则组件删了事件还寄生在window上。2.5 一张表看全生命周期钩子对比钩子函数Vue 3触发时机可访问数据典型使用场景beforeCreate实例初始化前数据不可访问极少用全局配置注入created实例初始化完成data/methods可访问初始化数据、非DOM依赖的异步请求beforeMount模板编译完毕、挂载前数据可访问DOM未生成极少用挂载前最后的调整mounted真实DOM挂载完成数据、DOM都可访问DOM操作、图表初始化、事件绑定beforeUpdate数据变化、DOM更新前可访问现有DOM状态获取更新前的状态、记录旧值updatedDOM更新完成可访问新DOM依赖最新DOM的逻辑注意不要改数据beforeUnmount组件销毁前实例功能仍可用清理定时器、解绑事件unmounted组件销毁后实例已失效清理DOM引用、销毁第三方实例这张表我自己写代码时偶尔也会翻一眼尤其是几个容易混淆的执行顺序问题查表比翻记忆靠谱。2.6 Vue 2与Vue 3的钩子名称对照项目从Vue 2升级到Vue 3时生命周期这块最容易改错的就是销毁阶段的命名。Vue 2叫beforeDestroy和destroyedVue 3改成了beforeUnmount和unmounted。除了名称变化Vue 3还新增了renderTracked和renderTriggered这两个调试用的钩子生产环境用的不多但调试响应式依赖关系时挺有用。另外Vue 3的Composition API里生命周期钩子都改成了带on前缀的函数onBeforeMount、onMounted、onBeforeUpdate、onUpdated、onBeforeUnmount、onUnmounted。用的时候注意一个关键区别这些钩子函数必须在setup()阶段同步调用不能放在异步回调里否则组件无法正确注册这些钩子。3. 组合式APIComposition API中的生命周期写法变了时机没变3.1 在setup里注册生命周期钩子Vue 3引入了setup()这个入口函数随之而来的是生命周期钩子用法的转变。以前是选项式API里平铺mounted()、created()、methods现在变成了在setup()中显式导入并调用onMounted、onUpdated这类函数。import { ref, onMounted, onBeforeUnmount } from vue export default { setup() { const count ref(0) onMounted(() { console.log(组件挂载完成count , count.value) }) onBeforeUnmount(() { console.log(组件即将销毁在这里清理资源) }) return { count } } }这种写法最大的好处就是逻辑聚合。以前一个功能要拆到data、created、mounted、methods、destroyed各个选项里改一个功能要上下翻代码眼睛都花了。现在一个功能模块的响应式数据、生命周期处理、方法定义可以写在一块阅读起来明显更舒服。3.2 setup语法糖script setup中的生命周期用法现在新开Vue 3项目绝大多数直接用script setup语法糖代码更精简。生命周期钩子的用法自然也跟着简化script setup import { ref, onMounted, onUnmounted } from vue const timer ref(null) const seconds ref(0) onMounted(() { timer.value setInterval(() { seconds.value }, 1000) }) onUnmounted(() { clearInterval(timer.value) }) /script template p已运行 {{ seconds }} 秒/p /template这个组件是个简单的计时器。onMounted里启动定时器onUnmounted里清除逻辑清清楚楚代码量也比选项式API少了不少。这里再补一个容易被忽略的细节script setup中onMounted这些函数是从vue包中显式导入的如果你忘了导入运行时会直接报错onMounted is not defined。我在把旧代码重构到script setup时最常遇到的就是这种低级错误建议在检查阶段多留意一下引入语句。3.3 父子组件生命周期执行顺序如果你在项目里同时用到父子组件那么生命周期钩子的执行顺序按这个逻辑走父组件beforeCreate→created→beforeMount子组件beforeCreate→created→beforeMount→mounted父组件mounted简单说就是父组件先完成初始化然后子组件逐层完成挂载最后父组件再完成挂载。销毁阶段刚好相反父组件先触发beforeUnmount子组件才会进入销毁流程。这个顺序在开发中直接影响你的逻辑设计。比如父组件mounted里依赖了子组件渲染的数据而子组件数据又是异步请求来的就会存在时序问题。我常用的解决办法是在子组件内部处理好数据后再通过$emit通知父组件或者使用defineExpose暴露子组件的方法给父组件调用根据场景选一种。4. 实际业务场景实操生命周期钩子的高频用法4.1 页面数据请求created还是mounted这是面试官最爱问的问题之一也是日常开发中最常见的需求“我有个列表页进页面就要拉数据请求到底放created还是mounted”我的观点很直接两者都能用但优先选mounted。虽然created在这里发请求也完全没有问题因为响应式数据已经准备好了this.$http也能调但问题在于请求发得太早初始模板甚至还没有渲染出来如果请求超时较慢用户看到的是空白页面体验略差。放在mounted里先把骨架渲染出来再发请求至少视觉上会舒服一点。实际开发中我见过不少团队约定统一放在mounted里我觉得这是比较合理的团队规范逻辑的一致性比“在created里省那几毫秒”更有价值。4.2 图表组件初始化必须等到DOM就绪用了ECharts、Chart.js这类图表库的同学一定对“DOM没准备好”这个痛点深有体会。初始化图表时document.getElementById(myChart)拿到的必须是真实存在的DOM节点而获取它的正确时机只能是在mounted之后。template div refchartRef stylewidth: 100%; height: 400px/div /template script setup import * as echarts from echarts import { ref, onMounted, onBeforeUnmount } from vue const chartRef ref(null) let chartInstance null onMounted(() { chartInstance echarts.init(chartRef.value) chartInstance.setOption({ title: { text: 月度销售 }, xAxis: { data: [1月, 2月, 3月] }, yAxis: { type: value }, series: [ { name: 销量, type: bar, data: [120, 200, 150] } ] }) }) onBeforeUnmount(() { if (chartInstance) { chartInstance.dispose() chartInstance null } }) /script这里最容易被忽略的操作是销毁阶段调用dispose()方法释放图表实例。ECharts实例注册了大量事件监听和定时器不清掉在频繁切换路由的长列表页面里就是妥妥的内存泄漏。这个问题在监控页、大屏项目里尤其容易踩因为图表数量多销毁不及时页面越用越卡。4.3 事件监听与定时器的正确清理姿势定时器、事件监听这类“副作用”生命周期钩子的经典应用就是onMounted里加onBeforeUnmount里减进出配对。script setup import { ref, onMounted, onBeforeUnmount } from vue const width ref(window.innerWidth) function handleResize() { width.value window.innerWidth } onMounted(() { window.addEventListener(resize, handleResize) }) onBeforeUnmount(() { window.removeEventListener(resize, handleResize) }) /script代码很简单但这里有几个细节值得注意addEventListener和removeEventListener的回调必须是同一个函数引用否则移除无效。你不能在mounted里传匿名函数然后在unmount里传另一个同内容但不同引用的匿名函数那样删不掉。定时器同理setInterval/setTimeout返回的句柄一定要保存下来销毁阶段才能准确清除。4.4 keep-alive带来的特殊“存活”生命周期项目里用到keep-alive包裹组件时生命周期钩子的行为会发生变化——组件被缓存后不会销毁而是会触发两个独有钩子activated激活时触发和deactivated失活时触发。这个场景最典型的就是后台管理系统的tab切换。页面A切到页面B不希望A里的滚动位置、搜索条件丢失用keep-alive缓存。但问题来了缓存导致onMounted只在第一次被触发后续切换不会重新执行而你的一些初始化逻辑可能需要每次激活时都刷新。解决办法是利用onActivated来做每次激活时的刷新逻辑onDeactivated用来做暂停逻辑比如停止视频播放、暂停动画、暂定定时器等。script setup import { onMounted, onActivated, onDeactivated, onUnmounted } from vue onMounted(() { console.log(首次挂载只执行一次) }) onActivated(() { console.log(组件被重新激活每次切换回来都会执行) // 刷新列表数据 }) onDeactivated(() { console.log(组件被缓存停用暂停正在执行的逻辑) // 暂停定时器 }) onUnmounted(() { console.log(真正销毁缓存被清除时才会执行) }) /script注意区分onUnmounted只有在组件真正被销毁时才会触发当keep-alive缓存还在时组件不会走销毁流程。5. 生命周期相关的常见问题与排查技巧5.1 在mounted中获取异步数据长度结果为0这个坑我估计几乎所有写过Vue的人都会遇到。场景是mounted里发请求获取列表数据然后试图拿到列表长度做判断结果拿到的是0或者undefined。原因很简单mounted触发时异步请求还没有返回数据根本还没填充进去。这不是生命周期时机不对而是异步请求的时序超出了生命周期的范围。我的经验是如果需要在数据更新后做事用watch监听数据源配合nextTick确保DOM也更新完成watch(listData, (newVal) { nextTick(() { console.log(列表长度是, newVal.length) // 这里可以做DOM测量类操作 }) })这样就不会出现“数据还没到就开始量DOM”的尴尬。5.2 updated里改数据导致死循环有些新手会为了“每次数据变化后都做点操作”直接在updated里写状态修改。结果就是循环触发更新页面白屏或卡死控制台一片报错。排查这类问题最重要的思路就是记住一条原则updated里只能读取和操作DOM不要修改响应式数据。如果确实需要在数据变化后做逻辑处理第一选择永远是watch——理论上它更精准只对你关心的数据变化做出反应不会像updated那样“所有数据一变就触发”。5.3 接口返回后组件已经销毁赋值报警告场景是这样的用户进入列表页后立刻点击跳转到详情页但列表页之前发出的请求还没有返回。数据回来后代码尝试给已经销毁的组件实例赋值Vue 3里会提示类似component is unmounted的警告。解决这个问题的思路有两种第一种在请求发起前加一个“组件是否存活”的标志位销毁时置为false请求回来判断一下再赋值let isAlive true onMounted(async () { const res await fetchList() if (isAlive) { list.value res.data } }) onUnmounted(() { isAlive false })第二种用AbortController取消请求const controller new AbortController() onMounted(async () { try { const res await fetchList({ signal: controller.signal }) list.value res.data } catch (error) { if (error.name AbortError) return } }) onUnmounted(() { controller.abort() })虽然Vue 3对这种情况的警告不像Vue 2那么严格但在大项目中还是建议养成清理异步请求的习惯对内存健康和代码健壮性都有好处。5.4 Vue 3中组合式API钩子注册失效的边界情况onMounted这些钩子一定不要在setup()的异步流程里调用比如setup() { setTimeout(() { onMounted(() { // 这样不行 console.log(不会按预期触发) }) }, 100) }这类写法会导致Vue无法正确注册钩子生命周期回调根本不会被触发而且大概率会直接给你抛异常。这是我亲自踩过的一个坑——当时在做一个“延迟初始化”的优化想等某个条件满足后再挂载钩子结果排查了很久才发现是这个调用位置的问题。5.5 使用provide/inject时的生命周期配合provide和inject在父子组件之间传数据很常用但和生命周期也有联动。父组件通过provide提供的数据会在父组件setup()执行时被封送子组件的inject在初始化时就能拿到。如果父组件后续对提供的数据做了响应式修改子组件里inject到的对象在自身生命周期内会保持更新前提是provide的值本身是响应式的。我遇到过一种情况父组件用provide传递一个非响应式的普通变量结果子组件里修改了但视图不更新。根因在于不知道provide普通值不是响应式的改成ref或reactive包装后就正常了。这也是不少人把生命周期和provide/inject一起用时常忽略的细节。6. 前端面试中生命周期题型整理与回答思路6.1 基础必背题生命周期流程与钩子顺序这类题是Vue面试的地基一般会这么问“请描述一下Vue组件的生命周期。”“Vue在数据更新时有没有办法在不触发重复渲染的情况下做一些事情”回答时最好按阶段拆分同时把每个钩子的关键使用场景带一下不要只背干巴巴的流程一、组件初始化阶段beforeCreate实例初始化前→created实例创建完成可以访问data和方法 二、模板渲染阶段beforeMount模板编译完成DOM未挂载→mountedDOM已经挂载可以进行DOM操作 三、更新阶段beforeUpdate数据改变DOM更新前→updatedDOM更新完成 四、销毁阶段beforeUnmount实例销毁前清理定时器/事件→unmounted实例销毁后把这条线串完整再把每个节点能做什么说清楚基本就合格了。6.2 进阶必考题创建请求为什么放在mounted而不是created面试官问这题其实是考察你是否理解生命周期与实际业务的结合。合理的回答思路是技术上两者都能发请求差异在于created更早但此时DOM尚未挂载mounted时DOM已生成配合骨架屏体验更好团队规范一般统一用mounted逻辑更一致补充如果要用async/await在mounted里请求需要注意异步返回后组件是否已经销毁防止给已卸载的组件赋值这样回答既显示了理解深度又证明了你有实际踩坑经验。6.3 陷阱题路由切换时组件销毁会引起什么问题一个完整的考察链是路由从A切到BA组件会经历哪些生命周期如果在A组件里有个全局事件监听会怎样正确答案A组件会执行beforeUnmount然后unmounted。如果A在mounted里监听了window的scroll或resize事件但在unmounted里没有移除那么监听仍然存在于全局用户滚动时还会触发已销毁组件里的回调轻则打印错误重则泄露内存。这道题还能引申出更实战的问题列表页做了请求在请求没有返回时用户切换了路由就会有我们上面说的“组件销毁后赋值”隐患。面试中能主动把这类问题梳理清楚非常加分。6.4 套路题keep-alive组件下的生命周期差异有经验的面试官一般不会直接问“生命周期有哪些”而是抛出业务场景列表页切换出去再切回来希望恢复滚动位置和搜索条件怎么办这类问题背后就是要考察你对keep-alive和activated/deactivated的了解。回答思路使用keep-alive包裹组件后组件会被缓存不会销毁需要恢复滚动条等操作时利用activated在每次重新进入时执行需要注意mounted只在首次进入时执行后续进入都是activated缓存组件在真正销毁时比如移除keep-alive或强制清除才会触发unmounted6.5 综合题Vue 2迁移Vue 3时生命周期改动这题这两年出现的频率越来越高毕竟大量项目都在做Vue 3升级。回答要点销毁钩子改名beforeDestroy→beforeUnmountdestroyed→unmounted新增调试钩子renderTracked、renderTriggered选项式API仍然支持但组合式API通过onXxx函数使用生命周期setup()中无法直接使用选项式生命周期钩子组合式API的钩子必须在setup()同步期间注册7. 一个完整示例用生命周期钩子实现一个可复用的数据刷新组件写了这么多原理和场景最后放一个我在真实项目里用过的组合示例。这个组件实现的功能是展示用户详情进入页面加载数据每隔30秒自动刷新对话页切走时暂停刷新切回来时立即恢复。完整串起来用到了onMounted、onBeforeUnmount、onActivated、onDeactivated、onUpdated这5个钩子。template div classuser-panel h3用户详情/h3 p姓名{{ userInfo.name || -- }}/p p积分{{ userInfo.score ?? -- }}/p /div /template script setup import { ref, onMounted, onBeforeUnmount, onActivated, onDeactivated, onUpdated } from vue const userInfo ref({}) let refreshTimer null let isActive true async function fetchUserInfo() { try { const res await fetch(/api/user/info) const data await res.json() if (isActive) { userInfo.value data } } catch (error) { console.error(获取用户信息失败, error) } } function startAutoRefresh() { if (refreshTimer) return refreshTimer setInterval(fetchUserInfo, 30000) } function stopAutoRefresh() { if (refreshTimer) { clearInterval(refreshTimer) refreshTimer null } } onMounted(() { fetchUserInfo() startAutoRefresh() }) onActivated(() { isActive true fetchUserInfo() // 重新回到页面时立即刷新一次 startAutoRefresh() }) onDeactivated(() { isActive false stopAutoRefresh() }) onBeforeUnmount(() { stopAutoRefresh() isActive false }) onUpdated(() { // 仅做一个状态变化提示不修改数据 console.log(用户信息已刷新更新时间, Date.now()) }) /script这个组件按以下生命周期逻辑运行第一步首次挂载时请求用户信息并启动30秒自动刷新定时器 第二步切到其他页面时组件若被keep-alive缓存触发deactivated暂停定时器同时把isActive置为false防止请求返回后修改已隐藏的视图数据 第三步切回来时触发activated立即拉取一次最新数据再重启定时器 第四步组件真正被销毁时触发beforeUnmount全局清理定时器避免内存泄漏。这样一套下来用户看到的数据始终是最新的页面切走过程中也不会产生重复请求和无效赋值。这个组件是我在处理“用户在线状态面板”时写的后续改聊天模块的心跳轮询也复用了同一套模式。把生命周期当成状态机来管理代码会稳定很多。8. 生命周期相关的常见问题速查表现象原因解决方法mounted里取DOM尺寸为0异步数据未渲染完成DOM宽度未撑开用watch监听数据变化配合nextTick或setTimeoutupdated里改数据导致卡死数据变化触发再次更新进入循环改用watch或确保updated里不在修改响应式状态组件销毁后请求返回异常回调里给已卸载实例赋值用isAlive标志位或AbortController取消请求定时器不执行或无限执行定时器没在销毁阶段清除onBeforeUnmount中clearInterval/clearTimeoutonMounted只执行了一次不再触发组件被keep-alive缓存用onActivated实现每次激活时的逻辑全局事件监听移除失败回调函数引用不一致移除时传了匿名函数保存回调引用保证移除时是同一个函数provide传值修改但视图不更新提供的是普通变量不是响应式数据用ref或reactive声明provide的数据onMounted在异步回调中注册不生效组合式API钩子必须在setup同步阶段注册调整代码结构在setup同步作用域中调用生命周期注册函数排查问题时可以先对照这张表定位方向。经验上大部分生命周期相关的bug本质都是“时机不对”、“引用不一致”或者“没有成对清理”三种原因中的一种顺着这个思路去查效率会高不少。9. 实操心得与项目复盘我自己带项目时踩过的最大一个坑发生在一个数据大屏项目上。页面上同时挂了二十多个ECharts图表每个图表都在onMounted里初始化正常情况下没问题。后来产品要求把整个页面用keep-alive缓存起来图表实例就被一并缓存了。开始没觉得有什么不对直到用户反馈从大屏切到其他页面再回来第一次切回来图表还能显示第二次就出现图表闪烁、卡顿甚至有些图表直接空白。排查过程花了很长时间。控制台看了一圈没有明显的报错后来把ECharts实例的监听器数量和DOM节点数打出来对比才发现每切一次页面图表实例的监听器数量都在翻倍增长。原因就是组件被keep-alive缓存后onMounted不再触发但也不会走onUnmounted销毁流程图表实例一直占据内存监听器不断累积。最终方案分两步在onDeactivated里调用chart.dispose()释放实例在onActivated重新执行echarts.init并重新设置数据。同时补了一个额外的判断如果是小时级长翻页的数据看板干脆不用keep-alive缓存直接用普通的路由切换并让组件正常销毁。具体选哪种要看业务上是否真的需要保留页面状态而不是所有页面一股脑缓存。这件事之后我对生命周期的理解明显有了质的转变生命周期钩子不只是“在某个时间点执行某段代码”它是一整套资源管理机制的一部分。数据请求、定时器、事件监听、第三方实例这些和外界发生关联的资源都要在合适的生命周期阶段做对应的获取与释放。项目里另一个常用技巧是封装一个useLifecycleLogger组合式函数在开发模式下打印所有生命周期钩子的执行时间方便调试组件挂载顺序和性能// useLifecycleLogger.js import { onBeforeMount, onMounted, onBeforeUnmount, onUnmounted } from vue export function useLifecycleLogger(componentName) { onBeforeMount(() console.log([${componentName}] beforeMount)) onMounted(() console.log([${componentName}] mounted)) onBeforeUnmount(() console.log([${componentName}] beforeUnmount)) onUnmounted(() console.log([${componentName}] unmounted)) }组件里就一行调用script setup import { useLifecycleLogger } from /hooks/useLifecycleLogger useLifecycleLogger(UserPanel) /script在调试父子组件挂载顺序时这个工具真的省了不少时间尤其是一个页面里嵌套了很多层组件光靠脑子理顺序真的容易乱。10. 给新手的生命周期学习路线建议如果这篇文章你只能带走三个点我希望是第一生命周期钩子触发的是“时机”不是“业务”。不要试图在一个钩子里写完所有逻辑什么时机做什么事想清楚了再写。第二资源进出一个都不能少。每个setInterval、addEventListener、echarts.init都要找到对应的清理时机。内存泄漏往往不是一次崩出来的而是反复切换、反复进出积累出来的卡顿。第三Vue 3的组合式API并不改变生命周期本身只是改变了书写方式。理解底层规律无论项目用的Vue 2还是Vue 3React还是小程序都能很快适应。想要系统掌握生命周期我的建议是按这个顺序练用简单的计数器组件把onMounted、onUpdated、onUnmounted都打一遍日志直观感受触发顺序给组件加keep-alive对比onMounted和onActivated的区别写一个定时器组件从mounted加到unmounted清亲手制造一次内存泄漏故意不清再用浏览器性能面板观察对比做一个ECharts图表的页面练习初始化与销毁的完整流程最后再去看源码和官方文档你会发现很多模糊的概念在这时候突然豁然开朗。这套路线也是我带新人时反复验证过的路径比直接啃文档效果好得多。生命周期这东西光看理论是记不住的亲手踩一次坑比你背十次官方文档都管用。
返回列表