Vue 组件与生命周期
涵盖 Vue 组件生命周期 8 个钩子、父子组件执行顺序、
keep-alive钩子差异、SSR 行为差异。
一、标准面试回答(1 分钟左右)
Vue 组件的生命周期是指组件从创建、挂载、更新到销毁的整个过程,每个阶段都有对应的钩子函数,让开发者可以在特定时机注入自定义逻辑。
创建阶段
beforeCreate 和 created。beforeCreate 是最早的钩子,此时组件实例刚创建,但数据观测、计算属性、方法等都还没初始化,拿不到 data、props 等。created 时,数据观测已经完成,可以访问 data、methods,但 DOM 还没生成,适合做接口请求、初始化数据。
挂载阶段
beforeMount 和 mounted。beforeMount 在模板编译完成、但尚未挂载到 DOM 时触发。mounted 时组件已挂载到页面,可以操作 DOM,适合做 DOM 相关操作、第三方库初始化。注意 mounted 不保证子组件全部渲染完成,必要时用 $nextTick。
更新阶段
beforeUpdate 和 updated。beforeUpdate 在数据变化、DOM 重新渲染前触发,适合在更新前访问现有 DOM 状态。updated 在组件 DOM 更新后触发,但要注意避免在 updated 中修改数据,容易导致无限循环。
销毁阶段
beforeUnmount(Vue 3)或 beforeDestroy(Vue 2)和 unmounted(Vue 3)或 destroyed(Vue 2)。销毁前钩子适合做清理工作,比如移除事件监听、清除定时器、取消请求等。销毁后钩子可以确认组件已从 DOM 移除。
Vue 3 中新增了 renderTracked 和 renderTriggered 用于调试响应式依赖,还有 activated 和 deactivated 用于 keep-alive 缓存的组件。
父子组件的执行顺序
父 beforeCreate → 父 created → 父 beforeMount → 子组件完整生命周期(从 beforeCreate 到 mounted)→ 父 mounted。更新时也是父先更新,子再更新;销毁时父先触发销毁前钩子,再递归销毁子组件。
二、20 秒极简版
Vue 生命周期分四个阶段:创建(beforeCreate、created)、挂载(beforeMount、mounted)、更新(beforeUpdate、updated)、销毁(beforeUnmount、unmounted)。父子组件顺序:父先创建但子先挂载,更新时父先于子,销毁时父先于子。
三、完整父子生命周期顺序
初始化阶段:
1. 父 beforeCreate
2. 父 created
3. 父 beforeMount
4. 子 beforeCreate → 子 created → 子 beforeMount → 子 mounted(递归执行所有子组件)
5. 父 mounted
更新阶段(父数据变化触发):
1. 父 beforeUpdate
2. 子 beforeUpdate → 子 updated(递归)
3. 父 updated
销毁阶段:
1. 父 beforeUnmount
2. 子 beforeUnmount → 子 unmounted(递归)
3. 父 unmounted核心规律:创建是父优先,但子先完成挂载;更新是父先开始,子先结束;销毁是父先开始,子先结束。这个顺序在调试组件渲染时机、处理依赖关系时很重要。
四、高频追问
追问 1:created 和 mounted 做接口请求的区别?
核心区别是 DOM 是否可用 和 执行时机早晚。
created 做请求:优势是执行更早,可以更快发起请求,适合不需要 DOM 操作的数据获取。比如获取列表数据,数据回来后再渲染,用户感知到的加载时间更短。缺点是此时拿不到 DOM 元素,不能做 DOM 相关的操作。
mounted 做请求:优势是可以同时操作 DOM,适合需要依赖 DOM 尺寸或位置的数据,或者需要初始化第三方库的场景。缺点是执行时机晚于 created,请求发出时间稍晚。
选择习惯:90% 的场景用 created,因为数据获取和 DOM 无关,越早发起请求越好。只有明确需要 DOM 时才用 mounted(比如获取元素宽度后去请求适配的数据)。另外要注意,服务端渲染(SSR)中 mounted 不会执行,必须在 created 或 beforeMount 中处理。
追问 2:keep-alive 缓存组件的生命周期有什么不同?
被 keep-alive 缓存的组件,不会走完整的销毁流程,而是多出两个钩子:activated 和 deactivated。
首次进入:created → mounted → activated
切换到其他组件:deactivated(不触发 unmounted)
再次切回:activated(不触发 created 和 mounted)
真正销毁时(如路由彻底离开或 keep-alive 缓存淘汰):才会走 beforeUnmount → unmounted使用场景:
activated:每次进入组件都需要执行的逻辑,比如刷新数据、重置滚动位置deactivated:组件被缓存时执行的清理,比如暂停轮询、停止动画
常见误区:把一次性初始化逻辑放 activated,导致每次进入都重复执行;或者把每次都要刷新的逻辑放 mounted,导致缓存恢复后数据不更新。正确做法是:首次加载的逻辑放 created/mounted,每次进入刷新的逻辑放 activated。
五、易错点
- 钩子名称混用:Vue 2 的
beforeDestroy/destroyed和 Vue 3 的beforeUnmount/unmounted说混 - 父子顺序记反:常见错误是"子组件 mounted 在父组件之前"顺序说反
- 忽略 keep-alive 的生命周期变化:只说基础 8 个钩子,不提
activated/deactivated - mounted 的坑不说:不主动提"
mounted不保证子组件完全渲染" - updated 中修改数据:这个坑很多人踩过,能主动提醒"
updated中修改数据会导致无限循环",说明有实战经验 - 服务端渲染的差异:SSR 下
mounted及之后的钩子不会在服务端执行 - 只背钩子名称,不说使用场景:能说出"beforeCreate 拿不到 data,适合做插件初始化"才显得有理解
六、Vue 2 vs Vue 3 钩子对照表
| Vue 2 | Vue 3 | 触发时机 |
|---|---|---|
beforeCreate | beforeCreate | 实例初始化后,数据观测前 |
created | created | 数据观测完成,DOM 未生成 |
beforeMount | beforeMount | 模板编译完成,未挂载 |
mounted | mounted | 已挂载到页面 |
beforeUpdate | beforeUpdate | 数据变化,DOM 重渲染前 |
updated | updated | DOM 更新完成 |
beforeDestroy | beforeUnmount | 实例销毁前 |
destroyed | unmounted | 实例销毁后 |
| — | renderTracked | 调试响应式依赖 |
| — | renderTriggered | 调试响应式触发 |
activated | activated | keep-alive 激活(详见 06 文档) |
deactivated | deactivated | keep-alive 缓存 |
八、Vue 模板编译原理(Template → HTML)
1. 什么是 Vue 的模板编译?
Vue 的模板(template)本质上是 HTML-like 的字符串,但浏览器不认识它。Vue 框架需要把这串字符串 编译成真实的 HTML 渲染函数(render function),这个过程就叫 模板编译。
2. 从 Template 到 HTML 的完整生命周期
Template(字符串)
↓ parse(解析)
AST(抽象语法树)
↓ optimize(优化)
带静态标记的 AST
↓ generate(代码生成)
Render Function(渲染函数代码字符串)
↓ new Function(...) 编译成真正的函数
Virtual DOM(VNode)
↓ patch(DOM Diff)
真实 DOM3. 这个过程是怎么实现的?(核心三步走)
第一步:解析(Parse)—— 将模板字符串转化为 AST(抽象语法树)
通过 HTML 解析器(如 parseHTML)扫描模板字符串,识别标签、属性、指令、文本,并通过栈结构构建节点间的父子关系,最终生成 AST(抽象语法树)。
第二步:优化(Optimize)—— 标记静态节点(静态提升)
遍历 AST,标记所有静态节点(不会随数据变化的节点)。后续渲染时可以直接跳过 diff,极大提升性能。
第三步:生成(Generate)—— 将 AST 转化为 Render 函数代码字符串
通过 generate 函数,将 AST 转换成类似如下字符串代码:
`_c(tag, data, children, normalizationType)`最终通过 new Function(code) 编译成可执行的 JS 函数。
4. 渲染阶段(将 Render 函数变成真正的 HTML)
执行 render 函数得到 VNode(虚拟 DOM 节点),再通过 patch 算法(详见 02-Vue Diff 算法)与旧的 VNode 对比,最小化操作真实 DOM。
5. 进阶加分(Vue3 的编译优化)
- Block Tree:Vue3 把动态节点收集到一个动态节点的数组(block),diff 时只对比 block 节点,跳过大量静态内容。
- PatchFlag:每个动态节点的 VNode 上带有 patchFlag(比如 1 表示 TEXT 变化,2 表示 CLASS 变化等),diff 时按需更新。
- hoistStatic:把静态节点提升到 render 函数外面,作为常量复用。
- cacheHandlers:事件函数自动缓存,避免每次 render 都重新创建函数引用。
九、关联文档
- 02-Vue Diff 算法
- 06-Vue scoped、keep-alive、插槽、过滤器(keep-alive 详解)
- 05-Vue ref、reactive、nextTick(nextTick 与 mounted 配合)