React 应用是由组件构建的,每个组件都有自己的生命周期。它出现在屏幕上,对新信息做出反应,最终消失。忽视这种节奏,代价是沉重的:内存泄漏会悄然滋生,渲染性能会下降。用户点击那些不再响应的按钮,是因为绑定在它们身上的组件在十分钟前就已经卸载了,但事件监听器仍然存活。理解组件生命周期并非一项学术练习,它是编写在实际使用中保持可靠的软件的基础。

将组件的存在分为三个阶段。开发者通常将其描述为诞生、成长和死亡。React 将其称为 mounting、updating 和 unmounting。每个阶段都有特定的任务,并为你提供了运行代码或进行清理的精确时机。

三个阶段

Mounting 是诞生。组件被创建并首次插入浏览器 DOM。这是你的初始化窗口。如果组件需要远程数据,这就是你请求数据的时候。如果它需要根据 props 计算初始 state,你也可以在这里完成。在类组件时代,这是 componentDidMount 的领地。在现代函数式组件中,你通过 useEffect 和一个空的依赖数组来处理它。关键细节是,这段代码会在 React 将元素放入页面后立即运行一次。这使得它非常适合进行一次性设置,例如获取用户资料或从 localStorage 中读取值。

Updating 是成长。已挂载的组件不会静止不动。来自父组件的 props 会到达,用户交互会导致 state 改变,父组件自身的重新渲染也会迫使子组件跟随。然后 React 会重新渲染组件,以保持 UI 与你的数据同步。这个阶段在组件的生命周期中会重复多次。每当依赖项发生变化时,你的 effect 就会再次运行,组件会将新的输出带回 DOM。由于这个阶段触发非常频繁,它也是大多数性能问题开始的地方。在这里进行的无用功会迅速累积。

Unmounting 是死亡。组件离开屏幕,可能是因为用户导航到了不同的页面,或者是条件渲染关闭了。此时,组件已被销毁,但外部世界并不总是知情。定时器仍在跳动,WebSocket 仍在监听,window 对象上的事件监听器仍然挂载着。如果你不清理这些内容,它们会持有对已销毁组件的引用并导致内存泄漏。浏览器会在这些“幽灵”上浪费 CPU 周期。React 为你提供 cleanup 函数,正是为了让你能在组件彻底消失之前停止定时器、关闭连接并移除监听器。

从类方法到 Hooks

多年来,React 工程师通过类组件来管理这些阶段。逻辑被分散在不同的方法中。你在 componentDidMount 中获取数据,在 componentDidUpdate 中响应 props 变化,并在 componentWillUnmount 中“打扫卫生”。问题在于相关的逻辑最终变得支离破碎。一个单一的功能(例如实时聊天连接)需要分布在三个独立的生命周期方法中,才能完成连接、检查线程变化和断开连接的操作。

Hooks 改变了这一点。函数式组件中的 useEffect hook 允许你按用途而非按生命周期阶段来对逻辑进行分组。尽管如此,如果你知道该看哪里,这种思维模型仍然可以清晰地对应。

要处理 mounting,请编写 useEffect(() => { ... }, [])。空的依赖数组告诉 React 该 effect 没有外部依赖,因此它仅在初始渲染后运行。这取代了 componentDidMount

要处理 updates,请包含 React 应该监听的具体值:useEffect(() => { ... }, [dependency])。当 dependency 在渲染之间发生变化时,