想象你负责安排一位新员工从入职到离职的完整旅程。入职第一天,HR 要先登记身份信息;然后带他参观工区、安排工位,让他正式亮相;工作中他不断接到新任务、调整工作方式;直到离职那天,他要交接项目、归还工牌、注销系统账号。组件的一生,和这段旅程惊人地相似:constructor 是身份登记,render 是首次亮相,componentDidMount 是正式上岗,componentDidUpdate 是成长变化,componentWillUnmount 是离职清理。React 和 Vue3 虽然写法不同,本质都在回答同一个问题——在组件的哪一刻,该做什么事。本文用这场“人生旅程”做主线,带你走完组件从创建、挂载、更新到卸载的全过程,并理清类组件、Hooks 与 Vue3 组合式 API 三种写法的对应关系。
掌握生命周期,你就掌握了在任何前端框架里“在对的时机做对的事”的能力。这是 React/Vue 高阶开发的基石,也是面试中最高频的考点之一。
可视化演示
展示React类组件和Vue3组合式API的生命周期钩子执行流程,以及父子组件生命周期的由内到外执行顺序。交互切换框架视图。
本节配有交互动效(进入视口后自动加载,约 3KB)。
React类组件生命周期 挂载:Con→Rnd→CDM 更新:SCU→CDU 卸载:CWU清理 componentDidMount() React生命周期分挂载/更新/卸载三阶段
生命周期总览
如果把组件比作一个人,那么它的一生和我们一样,可以粗粗分成三段:出生、成长、告别。出生时要办理户口、登记信息;成长中要读书工作、不断遇见新的人和事;到了告别那天,得把行李收好、把钥匙归还。组件的一生也是这样——它从构造函数中诞生,经历多次渲染更新,最终在页面上被移除。这些关键节点,就是生命周期。
在 React 和 Vue3 中,“生命周期”指的是组件从创建、挂载、更新到卸载的完整过程。框架并不会偷偷替你决定每个时刻该做什么,而是把主动权交给你:在某个节点上,如果你注册了一个函数,框架就会在恰当时机调用它。这种被框架回调的函数,就是钩子函数(hook)。钩子的英文 hook 原意是“挂钩”,像门上的衣帽钩,你挂什么,它就帮你挂住什么。
React 类组件把生命周期拆成一个个方法:constructor、render、componentDidMount、componentDidUpdate、componentWillUnmount。Vue3 组合式 API 则用 setup 加 onMounted、onUpdated、onUnmounted 等函数。虽然名字不同,但都在回答同一件事:挂载时做什么、更新时做什么、卸载前做什么。下面的公式概括了组件一生的主航道。
所以,学生命周期不用背一张大表格,只记住人生三大阶段就抓住了主线:出生办手续、成长做调整、告别做清理。接下来我们逐一走进这三大阶段,看看 React 和 Vue3 各自是怎么安排这场人生旅程的。
- Lifecycle(生命周期)组件从创建到销毁的完整过程,是框架管理组件状态的底层框架。
- Mount(挂载)组件首次被创建并插入真实 DOM 的阶段,类似人生“出生登记”。
- Update(更新)props 或 state 变化后组件重新渲染的阶段,类似成长中不断调整自己。
- Unmount(卸载)组件从 DOM 中移除、销毁前的阶段,类似告别前的行李整理。
React挂载阶段
一个人生命中除了生日,最重要的日子大概就是入职第一天。这一天要做的事格外多:HR 登记你的基础信息、你拿到工牌和电脑、被同事领着认路。组件也一样,它的“入职第一天”就是挂载阶段,从 constructor 到 render 再到 componentDidMount,一步都不能乱。
constructor 相当于身份登记:在这里初始化 state、把方法绑定到组件实例上,为接下来的生活准备好“行李”。render 则像打印工牌:它根据当前的 props 和 state,计算出组件长什么样,返回一份“描述图”(虚拟 DOM)。注意,render 必须是个“老实人”——只负责描述,绝不能偷偷做修改数据的动作。
等 render 把描述图交给框架,框架把真实 DOM 插入页面后,componentDidMount 才会被调用。这是组件“正式上岗”的信号:此时 DOM 已经真实存在,你可以放心地在这里发起网络请求、订阅事件、操作第三方库。之所以把请求放在这里而不是 render 里,是因为 render 每次更新都会执行,而 didMount 整个生命周期只执行一次。
挂载阶段就像入职第一天:constructor 登记身份、render 打印第一版简历、componentDidMount 正式上岗。搞清这三步,你就掌握了组件“出生”的全过程。
- constructor(构造函数)组件实例创建时第一个执行的函数,在这里初始化 state 和绑定方法,是“身份登记”环节。
- render(渲染函数)根据 props 和 state 输出虚拟 DOM 描述,必须保持纯净、不产生副作用。
- componentDidMount(挂载完成)真实 DOM 插入页面后触发,适合发起网络请求、订阅事件等副作用。
React更新阶段
人生不止入职那一天。进入公司后,你每天都会接到新任务、学到新技能,也会因为项目调整而改变工作方式。组件同样会“成长”,只要 props 或 state 发生变化,它就要重新渲染一遍,这个过程就是更新阶段。
更新的第一站是 shouldComponentUpdate。这个名字直译是“应该更新组件吗”,就像一个冷静的顾问在你面前问:这次变化真的需要我大动干戈、重新画一遍吗?如果数据和上次完全一样,回答 false 就能跳过这次渲染,省下宝贵的性能。
如果决定继续,render 会再次执行,根据最新数据生成新的描述图;随后 componentDidUpdate 在更新完成后被调用。在这里你常常需要对比新旧 props,判断是哪个数据引起了变化,再决定要不要做对应的副作用——比如数据筛选、图表重绘。值得注意的是,didUpdate 里直接用 setState 极容易造成死循环,务必加上条件判断。
更新阶段就是“成长的日常”:变化来了,先问值不值得重画,决定画了就好好画,画完再检查这次变化带来了什么。三步走,组件的成长之路就不会乱。
- shouldComponentUpdate(是否更新)在重新渲染前询问是否有必要,返回 false 可跳过本次渲染,是性能优化的闸门。
- render(重新渲染)用最新的 props 和 state 重新生成虚拟 DOM 描述。
- componentDidUpdate(更新完成)DOM 更新完毕后触发,可对比新旧值执行后续逻辑。
React卸载阶段
有相聚就有告别。离职那天,你要归还工牌、注销账号、交接手头项目。组件的最后一天就是卸载阶段,只有一场重头戏:componentWillUnmount。这是页面把组件移除前,最后一次给你打电话的机会。
很多人以为组件没了浏览器会自动收拾一切,其实不然。你在挂载阶段订阅的全局事件、启动的定时器、建立的 WebSocket 连接,都不会因为组件消失而自动消失——它们像离职后还在自动扣费的会员卡,继续消耗内存、触发回调。
所以 componentWillUnmount 是卸载前最后的清理窗口:clearInterval 清掉定时器、removeEventListener 摘掉监听、取消未完成的网络请求。错过这一步,轻则内存泄漏,重则出现“组件已卸载但回调还在改状态”的报错。在函数组件中,useEffect 返回的清理函数正是对应这一步。
如果把卸载阶段比作退租,componentWillUnmount 就是退租前的检查清单——关水、断电、还钥匙。每清理一项,内存就少一分负担,页面就多一分流畅。
- componentWillUnmount(卸载前钩子)组件从 DOM 移除前触发的最后一个钩子,整个生命周期只执行一次。
- 清理定时器clearInterval 停止后台定时任务,防止组件销毁后代码仍在执行。
- 移除监听与订阅removeEventListener 或取消订阅,切断组件与外部世界的联系。
useEffect替代
从 React 16.8 开始,生活有了新方式:函数组件不再需要 class,也能拥有生命周期。秘诀是一张“生活作息表”useEffect。它不像 class 时代把不同阶段拆成不同方法,而是用一套统一机制覆盖挂载、更新、清理三件事。
useEffect(fn, deps) 接收两个参数:一个函数 fn(你要做的事),一个依赖数组 deps(做这件事的条件)。每次渲染完成后,框架都会检查 deps:里面的值如果和上次不一样,就执行 fn;如果 fn 返回一个清理函数,就在下一次执行前或卸载前先调用它。
于是,传空数组 [] 就等于“只在挂载后做一次,卸载前做清理”,完美模拟 componentDidMount 加 componentWillUnmount;传 [count] 则意味着 count 变化时执行,类似 componentDidUpdate。这张作息表的精髓在于:你的生活规律(依赖)写得越诚实,组件的状态就越安全。
生活作息表把三个独立阀门合成一个旋钮:只要调对依赖数组,挂载、更新、清理尽在掌握。这也是目前 React 官方推荐的函数组件写法。
- 挂载后执行依赖数组为空时,仅在组件首次挂载后运行一次
fn。 - 依赖变化时执行数组里填的变量任一发生变化,
fn就重新运行,实现更新监控。 - 卸载前清理
fn返回的函数会在下次 effect 前和组件卸载前被调用,是清理副作用的出口。
Vue3组合式钩子
如果说 React 的函数组件是把手表揉进了一个函数,那么 Vue3 的组合式 API 则像是搬进一间“组合式公寓”——公寓不按房间分功能区,而是按你的需求自由组装:鞋柜放门口、书桌靠窗、健身器挨着阳台,一切都听从 setup 的统一调度。
setup 是组合式 API 的入口,相当于公寓的“总设计师”。组件创建时,setup 最先执行,负责创建响应式数据、把多个功能的逻辑组织在一起。它本身就是一个特殊的生命周期节点,执行的时机比 onMounted 更早。
在 setup 内部,你可以随时调用 onMounted(挂载完成)、onUpdated(更新完成)、onUnmounted(卸载前)来注册自己的钩子函数。注意,这些 onXxx 必须在 setup 期间同步调用,框架才能把它们登记到当前组件上。它们与 React 的生命周期一一对应:onMounted 对应 componentDidMount,onUnmounted 对应 componentWillUnmount。
组合式公寓的居住体验就是:不用再跑到“选项式”的各个房间里找对应方法,所有生命周期钩子都像家具一样在 setup 里按需组装。想挂载时做事?onMounted 安排;想卸载前清理?onUnmounted 收尾。
- setup(组合式入口)组件初始化时最先执行,创建响应式状态并注册钩子。
- onMounted(挂载完成)组件挂载完成后回调,适合请求数据、操作 DOM。
- onUpdated(更新完成)响应式数据变化导致重新渲染后触发。
- onUnmounted(卸载清理)组件卸载前调用,用来清理定时器与监听。
父子执行顺序
人生从来不是独角戏。我们生活在一个个家庭里,长辈与晚辈的先后次序,决定了家庭运转的节奏。组件也一样,父组件和子组件嵌套时,它们的生命周期钩子会按一套严格的“家庭规则”依次执行。
挂载时,父组件先进入 render——它要先把“户型图”画出来,中途发现需要子组件,就暂时停下来,让子组件从 constructor 开始完整地走完自己的挂载流程。子组件宣告挂载完成后,父组件才执行自己的 componentDidMount。这套顺序可以用一句话记:父先开始,子先完成,父后宣布。
更新阶段类似:父组件的 render 先执行,子组件随后重新渲染并完成 didUpdate,父组件的 didUpdate 最后落定。而卸载阶段则相反,子组件先 componentWillUnmount,父组件再卸载——先拆屋顶再拆承重墙,才不会砸到人。Vue3 中同样遵循“父 beforeMount → 子 mounted → 父 mounted”和“子 unmounted → 父 unmounted”的先后。
记住这条家庭规则,你就能准确预测任意嵌套组件中钩子的触发顺序,在排查“为什么子组件先请求”这类问题时不再两眼一抹黑。
- 挂载顺序父组件 render 启动渲染,子组件完整挂载完成后,父组件才执行 componentDidMount。
- 更新顺序父组件先重新渲染,子组件随后更新,父组件最后完成更新回调。
- 卸载顺序子组件先清理自己的资源,父组件再执行卸载,防止共享资源被提前释放。
框架异同对比
同一个人,可以过四种不同的生活:住酒店有酒店的服务流程,住公寓有公寓的管理规约。组件的生命周期在不同框架里也有四种“活法”,但背后的三阶段主线完全相同。
React 类组件把钩子写成方法:constructor、componentDidMount、componentDidUpdate、componentWillUnmount;React 函数组件用 useEffect 加依赖数组;Vue3 选项式保留 mounted、updated、beforeUnmount 等选项;Vue3 组合式则在 setup 里调用 onMounted、onUpdated、onUnmounted。四种写法、四套名字,映射到同一个生命周期坐标轴。
选择哪种写法,取决于项目和团队:老项目用类组件或选项式,迁移成本低;新项目推荐函数组件加 Hooks 或组合式 API,逻辑复用更强。但无论选哪种,理解“挂载做初始化、更新做响应、卸载做清理”这个底层模型,才能在任何框架中游刃有余。
四种活法就像同一座城市里不同的通勤方式:公交、地铁、骑行、步行。路线不同,目的地永远是同一个——把组件的每一个阶段管理好。作为开发者,挑一种适合当前项目的,同时理解其他几种,就能随时迁移不迷路。
- 类组件路线每个阶段对应一个独立方法,结构清晰但逻辑复用较难。
- Hooks 路线统一为 useEffect 加依赖数组,按逻辑组织而非按阶段组织。
- Vue3 路线onXxx 钩子在 setup 中注册,组合式地组织逻辑。
AI 应用场景
智能客服埋点与用户行为分析
阶段:数据采集与特征工程
解决的问题:大模型驱动的智能客服需要高质量的用户行为数据来训练话术。传统埋点把上报代码散落在各处,容易重复上报或漏报。用生命周期钩子把“会话开始/结束”集中管理:挂载时上报开始,卸载时上报结束,数据质量大幅提升。就像给人生旅程的两端各盖一个章,起点终点清清楚楚。
具体案例:某银行 AI 客服机器人页面,开发者在 onMounted 时上报“会话开始 + 渠道来源”,在 onUnmounted 时上报“会话结束 + 停留时长”。这些数据进入大模型训练管道,用于优化客服话术和意图识别准确率。
AI 实时质检大屏的 WebSocket 连接管理
阶段:在线推理服务的实时数据接入
解决的问题:AI 质检大屏的每个监控卡片组件都需要和推理服务保持 WebSocket 长连接,若不在卸载时断开,切换页面会导致连接堆积、内存泄漏。生命周期卸载钩子统一负责断开连接,保证资源随组件一起释放。
具体案例:某工厂部署 AI 瑕疵检测系统,每个机台是一个组件。组件挂载时建立连接订阅推理结果,卸载时断开连接、取消订阅。即使一屏几十个组件来回切换,前端内存依旧平稳,推理结果实时刷新不卡顿。
模型渲染性能监控
阶段:应用质量监控与优化
解决的问题:AI 应用往往携带重型模型库,渲染耗时直接影响用户体验。在 componentDidUpdate 里对比时间戳,可以精确算出每次更新的渲染耗时,发现异常及时上报给监控平台。
具体案例:某人脸识别 H5 应用,开发者在 componentDidUpdate 中计算前后渲染时间差,耗时超过 200ms 就上报。上线后提前定位到列表未加 key 导致的全量重渲染问题,修复后首屏时间下降 40%。
Python 演示
class SimulatedComponent: """用 Python 类模拟组件生命周期三个阶段""" def __init__(self, name): self.name = name self.props = {"count": 0} self.mounted = False print("[%s] constructor 出生登记" % self.name) def render(self): print("[%s] render 绘制界面 count=%s" % (self.name, self.props["count"])) def component_did_mount(self): self.mounted = True print("[%s] componentDidMount 正式上岗" % self.name) def component_did_update(self): print("[%s] componentDidUpdate 更新完成" % self.name) def component_will_unmount(self): print("[%s] componentWillUnmount 卸载前清理" % self.name) def mount(self): self.render() self.component_did_mount() def set_props(self, key, value): self.props[key] = value print("[%s] props 变化 %s=%s" % (self.name, key, value)) self.render() self.component_did_update() def unmount(self): self.component_will_unmount() self.mounted = False print("[%s] 已卸载" % self.name) class ParentComponent(SimulatedComponent): def mount(self): self.render() self.child = ChildComponent("Child") self.child.mount() self.component_did_mount() def set_props(self, key, value): self.props[key] = value print("[%s] 父组件 props 变化, 重新渲染" % self.name) self.render() self.child.render() self.child.component_did_update() self.component_did_update() def unmount(self): self.child.unmount() self.component_will_unmount() print("[%s] 父组件已卸载" % self.name) class ChildComponent(SimulatedComponent): pass if __name__ == "__main__": app = ParentComponent("App") app.mount() app.set_props("count", 1) app.set_props("count", 2) app.unmount()
class SimulatedComponent:定义一个“模拟组件”基类,用 Python 类复刻 React 生命周期def render(self):对应 React 的 render,只做描述不产生副作用def component_did_mount(self):对应 componentDidMount,挂载完成后执行def set_props(self, key, value):模拟 props 变化触发更新流程class ParentComponent(SimulatedComponent):父组件继承基类并重写挂载顺序self.child.mount()父组件渲染后创建子组件并让子组件先完成挂载self.component_will_unmount()卸载前执行清理逻辑,演示先子后父的顺序
常见误区
误区“我直接把数据请求写在 render 函数里不行吗?反正渲染时自然会请求一次,还能省一个钩子。”
正解render 是纯函数,每次更新都会重新执行。直接把请求写进去会导致:请求随每次 setState 重复发出,甚至形成“请求→setState→再请求”的死循环。正确做法是把副作用交给 componentDidMount / useEffect / onMounted。就像你写日记不该在照镜子时顺手写,而应在固定时间坐下来写。
为何常见:render 是最直观的“执行代码的地方”,初学者天然想往里塞逻辑,忽略了它会被反复调用。
误区“反正组件都要销毁了,浏览器会回收内存,定时器和监听器不用管。”
正解浏览器确实会回收 DOM,但定时器、事件监听、WebSocket、全局变量都属于组件外部资源,组件销毁后它们依然存活并持续消耗内存、触发回调。必须像“出门前关煤气”一样,在 componentWillUnmount 或 useEffect 清理函数里主动释放。
为何常见:简单页面看不出泄漏,等频繁切换页面后越来越卡顿,才后知后觉是清理没做。
误区“我把 useEffect 的依赖数组写成空数组,effect 只执行一次,闭包里读到的 props 一定是当前最新的。”
正解[] 只是让 effect 在挂载后执行一次,但闭包捕获的是首次渲染时的 props/state。如果里面有变化的变量,读到的永远是“第一次见面时的样子”,这就是经典的 stale closure 问题。解决方法是把用到的变量补进依赖数组,或用 useRef 保持最新引用。就像揣着一张旧地图去找新路,永远走不对。
为何常见:“只执行一次”的直觉正确,但闭包快照机制很隐蔽,没有踩过坑很难想到。
误区“树是从上往下建的,所以卸载时父组件先走、子组件随后跟上。”
正解React 卸载是自底向上的:子组件先执行 componentWillUnmount,父组件后执行。因为父组件的清理(如释放共享资源)必须保证子组件已经停止工作。就像拆除大楼先拆内部隔断、再拆承重墙,顺序反了会出事故。
为何常见:挂载时“父先开始、子先完成”的直觉,被错误地延续到了卸载阶段。
误区“Vue3 的 setup 里只能写响应式数据,以前那些 mounted、beforeUnmount 方法全没了,生命周期被砍掉了。”
正解生命周期依然存在,只是换成了组合式钩子函数:onMounted、onUpdated、onUnmounted、onBeforeUnmount 等。setup 本身就是“组合式入口”,相当于把生命周期注册从选项对象变成了可自由调用的函数,用起来更灵活,也更容易复用逻辑。
为何常见:选项式到组合式的 API 形态变化太大,让人误以为功能被删减,其实只是换了个房间住。
面试题
Q1. 组件挂载阶段,以下哪个执行顺序是正确的?
- A.constructor → componentDidMount → render
- B.render → constructor → componentDidMount
- C.constructor → render → componentDidMount← 正确答案
- D.componentDidMount → render → constructor
答案:C。挂载三连的顺序是固定的:constructor 先做身份登记(初始化 state),render 输出描述图,最后插入真实 DOM 后触发 componentDidMount。其他选项都打乱了“登记→绘制→上岗”的人生入职流程。
Q2. 组件卸载前,哪个钩子负责最后的清理工作?
- A.render 负责清理未完成的渲染任务
- B.componentWillUnmount 执行卸载前清理← 正确答案
- C.componentDidMount 负责挂载后的清理
- D.shouldComponentUpdate 执行渲染前清理
答案:B。componentWillUnmount 是组件销毁前唯一的清理窗口,负责清定时器、摘监听、断连接。render 是纯描述函数不应有副作用,componentDidMount 是挂载后的钩子,shouldComponentUpdate 是更新前的“闸门”。
Q3. Vue3 组合式 API 中,如何在挂载完成后执行逻辑?
- A.在 setup() 中调用 onMounted 注册回调← 正确答案
- B.在模板指令中绑定 mounted 回调
- C.在 store 里全局监听 mounted 事件
- D.在 props 中传入 mounted 处理函数
答案:A。组合式 API 要求在 setup() 内同步调用 onMounted 注册回调,框架才会把钩子登记到当前组件上。模板指令、store 全局监听、props 传函数都不是 Vue3 提供的生命周期注册方式。
Q4. 关于父子组件挂载顺序,正确的说法是?
- A.父组件 render 后子组件先完成挂载← 正确答案
- B.子组件渲染完成后父组件才 render
- C.父子组件完全独立并行执行挂载
- D.挂载顺序由组件代码量大小决定
答案:A。父组件先 render 发现子组件,暂停等待子组件完整走完 constructor→render→didMount,子组件宣告挂载完成,父组件才执行自己的 componentDidMount。“父先开始、子先完成、父后宣布”。
Q5. useEffect 的依赖数组为 [] 时,effect 会如何执行?
- A.每次渲染完成后都会重新执行
- B.仅挂载后执行一次且卸载前清理← 正确答案
- C.依赖数组为空则 effect 永不执行
- D.props 变化的一瞬间立即执行
答案:B。空数组表示“没有需要追踪的依赖”,effect 只在挂载后执行一次;如果它返回清理函数,则卸载前调用。它不会每次渲染执行,也不会永不执行,更不是同步立即触发。
Q6. Vue3 中与卸载阶段对应的组合式钩子是?
- A.onUnmounted 用于注册卸载前回调← 正确答案
- B.beforeUnmount 仅存在于选项式写法
- C.onDestroy 是 Vue3 官方卸载钩子
- D.onSetup 负责卸载前的状态保存
答案:A。组合式 API 用 onUnmounted 注册卸载前清理逻辑。beforeUnmount 确实是选项式的卸载钩子名,但题目问的是“组合式钩子”,且组合式里对应的是 onBeforeUnmount;onDestroy 是 Vue2 的历史叫法;没有 onSetup 这个钩子。
Q7. 在 componentDidUpdate 中调用 setState 时必须注意什么?
- A.任何情况下都可以安全调用直接更新
- B.必须加条件判断否则会陷入死循环← 正确答案
- C.每次更新只能调用一次否则报错
- D.不能在 componentDidUpdate 中使用 setState
答案:B。componentDidUpdate 里调用 setState 会触发新一轮更新,新一轮更新又会进入 didUpdate,形成死循环。因此必须先对比新旧 props/state,满足条件时才 setState。完全禁止过于绝对,无条件调用则必然出问题。
Q8. 以下哪些操作应该放在 componentWillUnmount 中完成?
- A.清除之前启动的定时器任务← 正确答案
- B.移除全局对象上的事件监听← 正确答案
- C.发起新的网络请求获取数据
- D.更新 state 触发最后一次渲染
答案:AB。卸载前要做的是“归还资源”:清定时器、摘监听、断连接。发起新请求属于挂载阶段的职责,而且卸载后请求返回也无处渲染;卸载时更新 state 违反 React 规范,组件即将销毁不应再触发渲染。
Q9. 关于 useEffect 的以下说法,正确的是?
- A.依赖数组控制 effect 的执行时机← 正确答案
- B.返回函数用于在卸载前清理资源← 正确答案
- C.依赖数组为空时每次渲染都执行
- D.effect 会阻塞浏览器页面渲染
答案:AB。依赖数组是 useEffect 的“开关”:空数组只在挂载后执行,非空数组在依赖变化时执行;返回的 cleanup 函数在下次执行前和卸载前调用。空数组不会每次渲染执行,effect 在渲染完成后异步执行,不会阻塞页面渲染。
Q10. 关于父子组件整个生命周期的全过程,下列说法正确的有?
- A.子组件先于父组件执行卸载清理← 正确答案
- B.父组件更新会引发子组件重新渲染← 正确答案
- C.父组件的挂载完成回调先于子组件执行
- D.卸载阶段可以安全地异步更新状态
答案:AB。卸载是“先拆屋顶再拆承重墙”,子组件先 willUnmount 父组件后执行,A 正确;父组件重新渲染时子组件必然跟随更新,B 正确;父组件的 componentDidMount 要等子组件全部挂载完成才执行,C 错误;卸载阶段已销毁的组件不应再更新状态,否则会告警甚至内存泄漏,D 错误。