组件的生命周期
React 类组件的生命周期可分为 挂载阶段( Mounting ) 、 更新阶段( Updating ) 、 卸载阶段( Unmounting ),以及 React 16.3+ 新增的错误处理阶段( Error Handling )。
一、挂载阶段(组件首次渲染到 DOM )
constructor():构造函数,初始化state或绑定事件处理函数(必须调用super(props))static getDerivedStateFromProps(props, state)(React 16.3 +):静态方法,在组件创建或更新前根据props更新state(返回需合并的state对象或是null)render():返回 JSX 描述的 UI 结构(纯函数,不修改状态或 DOM )componentDidMount():组件挂载后调用( DOM 已存在),适合执行副作用(如数据请求 、 定时器)
二、更新阶段(setState 或父组件重新渲染触发)
static getDerivedStateFromProps(props , state):同上,在更新前触发shouldComponentUpdate(nextProps, nextState):返回布尔值,决定是否跳过更新(优化性能,比较props/state变化)render():重新生成 UI 描述getSnapshotBeforeUpdate(prevProps, prevState)( React 16.3+):在 DOM 更新前捕获快照(如滚动位置),返回值传递给componentDidUpdatecomponentDidUpdate(prevProps, prevState, snapshot): DOM 更新后调用(可执行副作用,如根据新的 props 重新请求数据)
三、卸载阶段(组件从 DOM 树移除)
componentWillUnmount():组件卸载前调用(清理定时器 、 取消网络请求 、 移除监听事件)
四、错误处理阶段(子组件渲染出错时)
static getDerivedStateFromError(error):静态方法,捕获子组件错误并更新state(返回需合并的state)componentDidCatch(error, info):记录错误信息(如上报日志)
注意
React 16.3+ 后废弃了 componentWillMount 、 componentWillReceiveProps 、 componentWillUpdate (标记为 UNSAFE_ 前缀,推荐使用更安全的生命周期或 getDerivedStateFromProps 代替。
五、为什么 componentWillMount 被标记为不安全
在 React 18 中,componentWillMont 被标记为“不安全”( UNSAFE_ ),主要是为了配合 React 全新的 Fiber 结构 和 并发渲染 模式。
简单来说,这个生命周期方法在旧版 React 中表现尚可,但在 React 18 的并发模式下,它可能会引发严重的副作用和不可预测的 Bug。
1. 核心原因: Fiber 架构与可中断的渲染
在 React 15 之前,渲染过程是同步且不可中断的。但在 React 16+ 引入的 Fiber 架构中,渲染过程被分为了两个阶段:
- Render 阶段(渲染阶段) :主要是计算差异( Diff ),这个阶段是 可被中断、暂停甚至放弃的 。 React 可能会因高优先级的任务(如用户点击)插队,而打断当前的渲染,稍后重新渲染
- Commit 阶段(提交阶段) :将计算好的差异应用到 DOM 上,这个阶段是不可中断的,必须一气呵成以保证用户视觉连贯
而 componentWillMount 属于 Render 阶段。这意味着在 [React 18] 的并发模式下,他可能会被多次调用。
举个栗子
2. 具体带来的风险
由于 componentWillMount 可能会被执行多次,或者在渲染中中断执行,会导致以下严重问题:
- 副作用重复执行 :如果在其中发起网络请求或甚至定时器,这些操作可能会重复发生,导致数据不一致或资源浪费
- 内存泄漏与状态混乱 :这是一个非常隐蔽的 Bug。如果组件在
componentWillMont中订阅了外部数据源,但随后的渲染被中断(即componentDidMount从未执行),那么组件虽然没有挂载成功,但订阅却建立了。由于componentWillUnmount只有在componentDidMount执行后才能保证调用,这将导致无法取消订阅,从而引发内存泄漏 - 服务端渲染( SSR )问题 :在 SSR 环境中,
componentWillMount会在服务端执行一次,然后在客户端 hydration (注水)时再执行一次。如果涉及浏览器特有的 API(如window对象),会导致报错
3. 官方建议的代替方案
React 官方建议根据原本在 componentWillMount 中做的事情,将其拆分并迁移到更安全的地方:
原本在 componentWillMount 中操作 | 推荐替代方案 |
|---|---|
| 初始化 State | 直接在 构造函数( constructor ) 中初始化,或使用类字段语法 state = {...} |
| 订阅外部数据源/副作用 | 移至 componentDidMount 。这是最安全的时机,因为它保证只执行一次且 DOM 已挂载 |
| 异步数据请求 | 移至 componentDidMount 。虽然这可能会导致一次往外的渲染(先渲染空状态,再渲染数据),但能保证逻辑的健壮性 |