Sunday面试指南

Vue 3 生命周期有哪些?父子组件的执行顺序是什么?

下面是一段教学用的模拟面试。

🧑‍💻 面试官:Vue 3 的生命周期,你一般怎么理解?

🙋‍♂️ 我:组件创建后挂载,数据变化时更新,离开页面时卸载。在相应钩子里做初始化和清理。

🧑‍💻 面试官:父组件的 onMounted 执行了,是不是说明子组件请求的数据也准备好了?

🙋‍♂️ 我:不一定。挂载完成和异步请求完成是两件事。

🧑‍💻 面试官:那父子组件谁先 mounted?父组件只修改自己的一个数字,子组件又一定会执行 updated 吗?

这道题要看清两件事:组件走到了哪个阶段,以及这次变化到底涉及哪些组件。

面试速答(60 秒版)

Vue 的生命周期,就是一个组件从创建、挂载、更新到卸载的过程。生命周期钩子让咱们在这些阶段安排工作,例如挂载后访问 DOM,卸载时清理计时器和事件订阅。

对于普通的同步父子组件,父组件先开始挂载,等子组件挂载完成以后,父组件的 onMounted 才执行。如果一次更新同时涉及父子组件,父组件先进入更新,子组件先完成更新,父组件再执行 onUpdated。

不过,父组件更新不代表所有子组件都更新,onMounted 也不代表接口请求已经结束。异步组件、KeepAlive 和服务端渲染还有各自的边界,回答顺序时需要先说明讨论的场景。

生命周期四个阶段及对应工作

知识点详解:从打开一个页面,到关闭它

先把一个组件自己的过程走清楚

假设咱们有一个资料页面,父组件负责页面标题,子组件显示用户信息。

页面需要这个组件时,Vue 先创建组件实例,准备响应式数据、计算属性和渲染需要的内容。使用组合式 API 时,这些初始化工作通常写在 setup 或 script setup 里,没有一个需要额外注册的 onCreated。

接下来,Vue 根据当前数据生成并插入 DOM。onBeforeMount 发生在首次挂载前,onMounted 则发生在当前组件完成挂载后。因此,需要访问组件里的真实元素时,通常放在 onMounted,而不是刚进 setup 就去找元素。

数据变化后,如果这次变化影响组件的渲染,Vue 会安排更新。onBeforeUpdate 可以看到更新前的 DOM,onUpdated 则在这次组件 DOM 更新以后执行。这里说的是组件更新,不是任何一个普通变量赋值都会触发钩子。

最后,组件被卸载。onBeforeUnmount 执行时,实例还可以使用;onUnmounted 执行时,子组件已经卸载,组件关联的响应式副作用也已停止。但手动创建的计时器、外部事件监听等,仍然要由开发者安排清理。Vue 生命周期 API给出了这些阶段的具体约定。

父子顺序,为什么不是“父组件全部做完,再轮到子组件”?

父组件的模板里包含子组件。Vue 要完成父组件的挂载,就需要先处理这段子组件树。

所以,普通同步父子组件的首次挂载可以按这条线理解:

父 onBeforeMount
  → 子 onBeforeMount
  → 子 onMounted
  → 父 onMounted

父组件先开始,子组件先完成。父组件的 onMounted 因而可以在这个范围内访问已经挂载的同步子组件。

但如果子组件是异步组件,或者位于 Suspense 的异步分支里,就不能把上面的顺序当成“父 mounted 会等待所有异步内容”。子组件 mounted 后发出的请求,也不会自动被父组件等到。

假设子组件在挂载时查询用户信息,请求还需要一秒返回。那么父组件已经 mounted,而页面里的用户姓名仍然是加载状态,完全正常。要知道数据准备好了,应通过请求结果、状态或明确的通知判断。

同步父子挂载的嵌套顺序,挂载与请求完成分离

更新顺序,先问这次有没有更新子组件

假设父组件把 userName 作为 prop 传给子组件。userName 变化以后,父组件和子组件都需要更新显示内容。

对这个同步更新场景,顺序通常是:

父 onBeforeUpdate
  → 子 onBeforeUpdate
  → 子 onUpdated
  → 父 onUpdated

这解释了为什么父组件的 onUpdated 发生在相关子组件更新之后。

那么,父组件只修改了自己的标题,传给子组件的内容没有变化呢?

子组件未必需要更新。Vue 会根据组件依赖和更新条件决定哪些组件参与,不能把“父发生更新”理解成“整棵树每个 updated 都执行一次”。

同一个组件的多次数据修改也可能被合并到一次更新里。如果只想等待某一次状态修改对应的 DOM 更新,nextTick 往往比在一个通用 onUpdated 回调里猜测原因更直接。也不要在 onUpdated 里无条件修改它依赖的状态,否则可能不断触发后续更新。

父组件私有状态与子组件 prop 更新的不同范围

卸载时,清理应该放在哪里?

假设页面离开时,父组件和同步子组件一起卸载。父组件先进入卸载流程,子组件卸载完成后,父组件才完成自己的卸载:

父 onBeforeUnmount
  → 子 onBeforeUnmount
  → 子 onUnmounted
  → 父 onUnmounted

如果子组件启动了轮询,就由子组件保存轮询句柄,并在自己的卸载钩子里取消。这样父组件不需要知道每个孩子内部创建了哪些资源。

使用 KeepAlive 缓存组件时,离开当前显示位置还可能只是停用,而不是卸载。需要在停用时暂停的任务,要考虑 onDeactivated;只写 onUnmounted,可能还没有到你期待的清理时机。

卸载顺序与手动资源归属

顺序题,可以用一次日志实验确认

给父子组件的相关钩子分别加上带身份的日志,然后测试首次打开、修改传递给子组件的 prop、只修改父组件私有状态,以及卸载整个组件树。

这几次操作要分开做。日志里出现一个“updated”,需要知道它属于谁、由哪次状态变化引起,不能把多个操作的输出拼成一条生命周期口诀。

如果项目有异步组件、条件渲染或缓存,再增加对应用例。复杂组件树的实际顺序,应结合自己的场景检查,别拿一个同步父子实验覆盖所有情况。

面试官继续追问

onMounted 执行以后,用户已经看到画面了吗?

它说明组件挂载完成,不能直接作为浏览器已经把这一帧显示到屏幕上的保证。DOM 更新和浏览器绘制还有时序区别,需要精确讨论视觉效果时,继续检查绘制调度。

生命周期钩子里可以 await 请求吗?

可以写异步逻辑,但不要理解成 Vue 会因此暂停后续所有生命周期。请求完成以后再修改状态,可能产生新一次更新;离开页面后还要避免旧请求结果继续影响当前页面。

服务端渲染也会执行 onMounted 吗?

不会在服务端渲染阶段执行。依赖浏览器 DOM 的逻辑应留在客户端相应阶段。服务端数据准备有另外的入口,例如 onServerPrefetch,不能混用两套运行环境。

面试速记卡

  • 生命周期:创建、挂载、更新、卸载,各阶段承担不同工作。
  • 同步父子挂载:父先开始,子先 mounted,父再 mounted。
  • 更新范围:父更新,不保证所有子组件都更新。
  • 完成条件:挂载完成不等于请求完成,也不直接保证屏幕已绘制。
  • 清理:卸载与 KeepAlive 停用分别判断,手动资源明确释放。

公司面试真题

真题根据求职者公开面经整理,题意经过概括,非逐字原话或公司官方题库;本文为 Sunday 的独立解析。

  • 小米 · 前端 · 原帖未明确批次

    Vue 3 有哪些生命周期钩子?(题意整理)

    小米前端二面 ↗
    原帖编辑于 2024-05-29

  • 腾讯 · 前端(TEG / QQ音乐 / PCG) · 暑期实习

    Vue 各生命周期阶段分别做什么?(题意整理)

    腾讯暑期实习前端面经 + 总结 ↗
    面试记录为 2020 年 3 月;原帖编辑于 2020-04-19

浏览公司面试真题 →
简历汪永久免费在线制作简历,模板直接套用、导出无水印,永久免费、下载免费,不需要付费解锁任何功能。去写简历