本节摘要:组件从创建、更新到销毁会经过一系列可预期的节点,即生命周期。把握好"数据加载放在挂载后、清理资源放在卸载前"这类时机,比硬背生命周期命名更有价值。本节把通用思想讲清,并预告 Hooks 时代的形态,具体语法交给第 3、4 章。
本节阅读目标
阅读完本节,你应当能够:
组件不是一段永恒竖立的代码。随着它被创建、刷新、移除,它对外部资源的占用也在变化:发起网络请求、订阅事件流、申请定时器……如果不蹭着这些节点收尾,就会造成泄漏。生命周期恰是框架留给你的"在每个关键时机做事"的钩子。
把三个阶段的"该做什么/别做什么"画成一张全景图。

评审是否合理,重点是时机而不是存不存在。三条法则:
类组件把生命周期写成一串方法可读的名字与回调,早期前端都在用。React 的 Hooks 时代则用 useEffect 这类副作用 Hook 表达同一回事,把"时机"包装成了回调。Vue 类似:Options API 里的 beforeMount、mounted 等方法对应 Composition API 里的 onMounted 等。表面是两套拼写,里子都是这套"创建 → 更新 → 销毁"心智。
用 React Hook 写一个带订阅清理的例子,把"就地释放"这条法则落成看得见的代码:
function OnlineBadge({ userId }) { useEffect(() => { const off = subscribeOnline(userId); // 挂载后/依赖变化后建立订阅 return () => off(); // 卸载前对称释放,正是"就地在对称节点清理" }, [userId]); // 依赖数组:userId 变了才重建订阅 return <span>在线状态订阅中…</span>; }
这个函数组件你看不到 mounted/unmounted 这些名字,但 useEffect 首次运行对应"挂载后"、依赖数组变化后重跑对应"更新"、返回的清理函数对应"卸载前"。同一份时机,换了件 Hook 的外衣——这正是"表不同、里一样"最直接的证据。Vue 的 watchEffect、onUnmounted 也只是把这套心智又换了个拼写。
把时机法则变成可执行的 checklist,每次写"要在某时刻做事"的组件时,按下面顺序走一遍就不会漏。先问"这件事是不是有副作用"——网络、订阅、定时器、改外部 DOM 都算,纯计算不算;若是副作用,再问"它在数据变化后还要不要重新做"——要则放进依赖驱动的回调里,不要则只在挂载时做一次;最后问"组件消失时要不要回收"——要就把回收函数也放在同一个对称点写干净。
拿这个顺序去套典型场景:数据请求跟着 id 变、又在卸载时作废——就是"副作用 + 按依赖重建 + 就地回收";一次性初始化 WebSocket 握手——就是"副作用 + 只挂载一次 + 卸载时断连";一个 DOM 尺寸监听——就是"副作用 + 别乱重建 + 记得移除"。三个问题问完,绝大多数"该在哪个钩子写"的纠结自然消失,比硬记十个钩子的名字靠谱得多。
这些坑的共性是"把时机写岔了",跟生命周期本身关系不大。真正能帮你的是始终带着三阶段视角:这段代码在挂载/更新/卸载时各该在哪一步跑,跑完要不要就地收尾。
生命周期不好记,是因为名字多、还老变。换个比喻一切顺了:把组件想成一次有借有还的"入场"。进场(挂载)时你借了什么,离场(卸载)时就还什么——订阅了得退订,定时器得清掉,请求的监听得拆除。这笔账在 React、Vue 里都一样,只是记账的语法不同。
什么时候这个类比会失效?当组件"还在场但数据源已经换人"时——比如下拉框从用户 A 切成用户 B,挂在同一组件里。此时不是"进场/离场",而是"换了个服务对象"。React 里靠依赖数组,Vue 里靠 watchEffect 的依赖变化,都让"订阅跟着服务对象走"。于是生命周期从不只是"挂载/卸载"两个端点,而是还包括"更新"这条中间轨道:它决定你的副作用在对象变化后要不要重建。把"入场→换人→离场"当成一条完整的主线去理解,比背一张表格更贴近真实编码时的取舍。
这种"有借有还"的眼光还能帮你判断一段副作用该不该上钩子:凡是"会占用外部资源、且组件没了就该归还"的逻辑,都值得放进生命周期/副作用钩子,并在卸载点写好归还。而纯计算、纯展示的逻辑,离钩子越远越清爽。判断的出发点永远是"这笔资源是谁借的、组件消失时要不要还",而不是某个钩子的名字。
生命周期在 React 与 Vue 有各自的具体钩子名,第 3、4 章的对应节会逐个落地。下一节先讲两类大家日常最频繁碰到的界面行为:事件处理,以及条件、列表渲染。