跳到主要内容

componentWillMount

2024年03月06日
柏拉文
越努力,越幸运

一、认识


componentWillMountReact 类组件早期的生命周期方法之一,它在组件挂载前调用,主要用于在初次渲染前进行一些最后的准备工作。不过,随着 React Fiber 架构和并发模式的发展,这个方法存在一些问题,已经被官方标记为不安全(UNSAFE_componentWillMount)并逐步废弃。

React Fiber 架构实现了 Fiber 将渲染模式从原来 不可中断的同步任务改为可中断的异步任务, 任务单元之间的任意切换和任务之间的打断和恢复等等。Fiber 架构下的异步渲染, 导致了 componentWillMountcomponentWillReceivePropscomponentWillUpdate 三个生命周期在实际渲染之前可能会被调用多次,产生不可预料的调用结果。因此这三个生命周期函数不建议被使用,取而代之的是使用全新的两个生命周期函数 getDerivedStateFromPropsgetSnapshotBeforeUpdate

React Fiber 架构下, 基于 Schedule 调度之后, 开始进行 Render 调和Commit 提交Render 调和阶段 的主要目标是生成一棵新的 Fiber 树,并为后续的 DOM 更新准备好 副作用列表(effect list。这个阶段不直接操作 DOM,而是进行计算和比较。render 阶段以 DFS 深度优先 的顺序遍历 ReactElement, 如果有子节点, 遍历子节点, 如果没有子节点遍历兄弟节点。Render 阶段 可以中断和恢复。Commit 提交阶段React 更新流程中唯一会直接操作 DOM 的阶段,它根据 Render 阶段生成的副作用列表执行实际的更新操作。这个阶段是同步执行的,确保所有副作用在一次提交中完成, 这个阶段是同步且不可中断的,确保 UI 的最终状态与最新的 React 元素描述完全一致。简单来说,由于 Render 阶段的操作对用户来说其实是不可见的,所以就算打断再重启,对用户来说也是零感知。而 commit 阶段的操作则涉及真实 DOM 的渲染,所以这个过程必须用同步渲染来求稳。

由于 Render 阶段 可以中断和恢复, 因此, 这就导致 render 阶段的生命周期都是有可能被重复执行的。componentWillMountcomponentWillReceivePropscomponentWillUpdate 都是在渲染阶段调用的,而不是在提交阶段调用。因此,如果渲染过程被中断或重启,这些方法就有可能被执行多次。由于这些方法会重复调用,如果其中包含了副作用操作(例如直接修改组件状态、调用 API 等),就可能导致意外的行为或性能问题。这也是为什么 React 官方建议尽量避免使用这些生命周期方法,并将它们标记为“不安全”(UNSAFE_)。

那么, 为什么 getDerivedStateFromPropsgetSnapshotBeforeUpdate 可以呢?

  • getDerivedStateFromProps, 它在 Render 阶段调用,每次组件更新(包括挂载和更新)时都会在 render 之前被调用,用来根据新的 props 计算并返回新的 state。它是一个静态方法,不依赖于组件实例,因此无法直接访问或修改实例属性,也就不会产生副作用。它根据当前 propsstate 计算出新的 state,只是返回一个对象供 React 合并,完全不会触发额外的副作用。即使在并发模式下 render 可能会被中断或重复调用,由于它是纯函数,多次调用不会对组件产生不一致的影响。

  • getSnapshotBeforeUpdate, 在 render 之后、DOM 更新之前调用,用于捕获(snapshot)一些即将更新的 DOM 信息。捕获的信息会传递给 componentDidUpdate,以便在 DOM 更新后使用。它同样不应引入副作用,只用于读取数据。它只读取并返回数据,而不会直接引入副作用, 没有引起状态或数据的直接修改。由于其仅用于 读取 操作,多次调用也不会对最终结果产生负面影响。