Vue 组件生命周期有哪些?Vue3 中如何使用?
简化版
Vue 生命周期描述组件创建、挂载、更新、卸载的过程。Vue3 Composition API 中常用 onMounted、onUpdated、onUnmounted、onBeforeMount、onBeforeUnmount 等钩子,副作用资源要在卸载时清理。
详细版
常见阶段:
- 创建:初始化 props、state、响应式依赖。
- 挂载:DOM 插入页面,
onMounted可访问 DOM。 - 更新:响应式数据变化后重新渲染。
- 卸载:组件从页面移除,清理事件、定时器、订阅。
Vue3 示例:
onMounted(() => {
window.addEventListener('resize', onResize);
});
onUnmounted(() => {
window.removeEventListener('resize', onResize);
});
面试重点不只是背钩子名,而是说清每个阶段适合做什么。
完整版教学
一、生命周期是组件资源管理模型
组件不是只渲染一次 HTML。它会创建状态、绑定事件、请求数据、响应更新,最后卸载。生命周期钩子给开发者在关键阶段插入逻辑的机会。
二、mounted 阶段适合访问 DOM
在 setup 执行时,DOM 还没挂载。需要测量元素尺寸、初始化图表、绑定依赖真实 DOM 的第三方库,通常放在 onMounted。
const el = ref(null);
onMounted(() => {
console.log(el.value);
});
三、updated 阶段要谨慎
onUpdated 在组件 DOM 更新后执行。如果在里面继续修改触发当前组件更新的数据,可能造成循环更新。
更常见的做法是用 computed/watch 精准处理数据变化,而不是在 updated 里做大量业务逻辑。
四、unmounted 阶段负责清理
定时器、全局事件、WebSocket、第三方库实例、订阅都要在卸载时清理。
这是前端内存泄漏高发点。组件创建了什么外部副作用,组件卸载时就应该释放什么。
五、面试追问与工程落地
生命周期常见追问是“setup 在哪个阶段”。在 Vue3 中,setup 发生在组件实例创建阶段,早于 mounted,里面还不能直接访问真实 DOM。它更像组合式逻辑入口,用来创建响应式状态、注册生命周期、准备方法。
还会问父子组件生命周期顺序。通常创建和挂载时,父组件先开始创建,子组件挂载完成后父组件才 mounted;卸载时父组件先触发 beforeUnmount,再卸载子组件,最后完成 unmounted。理解顺序有助于排查父子 DOM、订阅清理问题。
工程里副作用一定要成对管理:mounted 里添加的 window 事件,unmounted 里移除;创建的图表实例,卸载时 dispose;开启的轮询,卸载时 clear。生命周期的核心不是背钩子,而是资源生命周期闭环。
六、用父子时间线和资源闭环分析钩子
生命周期顺序应按“开始父工作 → 完成子树 → 完成父工作”理解,而不是孤立背名字。首次挂载时父组件先进入挂载准备,子组件完成挂载后父组件才 mounted;卸载则由父组件发起,清理整个子树后父组件最终完成卸载。具体业务不要依赖同级组件的偶然顺序,应通过数据流或显式事件协调。
挂载:父 beforeMount
└─ 子 beforeMount → 子 mounted
└──────────────→ 父 mounted
卸载:父 beforeUnmount
└─ 子 beforeUnmount → 子 unmounted
└────────────────→ 父 unmounted
| 阶段 | DOM 状态 | 合适工作 | 常见风险 |
|---|---|---|---|
setup | 尚无已挂载 DOM | 建状态、注册 hooks | 提前访问模板 ref |
onMounted | 自身 DOM 已创建 | 测量、初始化 DOM 库 | 忘记 SSR 不执行该钩子 |
onUpdated | 本轮 DOM 已更新 | 极少量更新后观察 | 再改依赖造成循环 |
onUnmounted | 组件已卸载 | 清理计时器、订阅和实例 | 资源泄漏 |
onActivated/deactivated | KeepAlive 缓存切换 | 暂停/恢复缓存页任务 | 误以为会 unmounted |
假设路由往返页面 50 次,每次 mounted 都新增 1 个 window resize 监听器却不移除,最终一次 resize 可能执行 50 份旧回调。把 add/remove、subscribe/unsubscribe、start/stop 成对放进组件生命周期,才能让资源数量始终回到 0 或 1。
异步请求也要考虑组件卸载。请求完成后若组件已离开,结果不应继续驱动无效逻辑;可以使用 AbortController、清理标记,或交给具备缓存和取消策略的数据层。生命周期钩子能管理资源边界,但不能自动取消所有异步工作。
onMounted(() => window.addEventListener('resize', onResize))
onUnmounted(() => window.removeEventListener('resize', onResize))
移除监听时事件目标、事件名和函数引用必须与注册时一致,因此不要在两处各写一个新的匿名函数。计时器、订阅和第三方实例也遵循同样的“保存句柄、原样释放”原则。
记忆钩子:组件创建了什么外部资源,就必须定义它在停用或卸载时如何释放;生命周期本质是资源所有权,不是钩子名考试。
七、常见误区与追问
- 误区:setup 执行时模板 DOM 已经可用。 setup 是状态和逻辑准备阶段,模板 ref 通常要到 mounted 后才指向真实元素。
- 误区:onUpdated 适合集中修改所有更新后的状态。 在其中继续修改参与渲染的数据可能触发新的更新甚至循环,应优先 computed 或精确 watch。
- 误区:KeepAlive 切走组件一定会触发 onUnmounted。 缓存组件通常进入 deactivated,真正从缓存移除时才卸载。
- 追问:父子 mounted 顺序是什么? 父先开始挂载流程,子 mounted 后父 mounted,确保父完成时其同步子树已挂载。
- 追问:为什么全局事件一定要清理? 事件目标生命周期长于组件,遗留回调会持有组件数据并重复执行,形成泄漏和逻辑错误。
- 追问:SSR 中 mounted 会执行吗? 服务端没有真实 DOM,mounted 类客户端钩子不在服务端渲染阶段执行,DOM 逻辑必须放客户端。
- 追问:请求应该放 onMounted 还是 setup? 取决于是否依赖 DOM、SSR 和数据框架策略;请求并不天然必须等 mounted,应明确执行环境和取消方式。
八、加强记忆
生命周期不要死背,按职责记:mounted 访问 DOM,updated 看更新后结果,unmounted 清理副作用。创建和释放成对出现,是组件稳定性的关键。
再按父启动、子完成、父完成的顺序推演同步子树,遇到 KeepAlive 和 SSR 时单独说明边界。