学 React 有两条路:一条是只学 API,遇到问题查文档;另一条是先弄懂它的更新机制,然后大部分「奇怪行为」都变成理所当然。

这个专题走第二条。它的价值不在于让你能背出 Fiber 的架构图,而在于把原理和你每天写的代码对应起来——为什么列表的 key 不能用索引、为什么 setState 之后立刻读 state 拿到的是旧值、为什么给每个函数都套 useCallback 反而更慢。这些问题的答案都在下面三层机制里。

学习路径

第一层:虚拟 DOM。 React 不直接操作真实 DOM,而是先生成一棵 JS 对象树,和上次的树比对,只把差异应用到真实 DOM。先理解这个缓冲层的存在,以及它的代价——虚拟 DOM 不是「更快」,而是「在提供声明式写法的前提下,性能不至于太差」。

第二层:diff 算法。 逐棵树完整比对的复杂度是 O(n³),不可用。React 靠三个假设把它降到 O(n):只比同层、类型不同直接重建、同层用 key 标识身份。key 不能用数组索引,根源就在第三条——索引会在增删时错位,导致 React 认错节点,进而出现状态串位和无谓重渲染。

第三层:Fiber。 React 核心算法的重写,目的是把渲染工作切成小块、分散到多帧执行,让高优先级的交互不被长任务阻塞。理解了增量渲染,就能理解为什么 React 的更新是「可中断」的。

第四层:落到日常 API。 生命周期与 Hooks、setState 的批量合并、状态管理选型、组件复用的三代方案(HOC → render props → Hooks)。

三代组件复用方案对比

方案 复用什么 主要问题 现状
Mixin 任意成员 命名冲突、来源不明 已废弃
高阶组件 HOC 组件逻辑 嵌套地狱、props 来源难追踪 少量场景仍适用
Render Props 组件逻辑 回调嵌套、JSX 结构变形 被 Hooks 大量替代
自定义 Hook 状态逻辑 有使用规则约束 当前首选

常见误区

以为虚拟 DOM 比直接操作 DOM 快。 不是。手写的精准 DOM 操作一定比虚拟 DOM 快。虚拟 DOM 的价值是让你用声明式的方式写 UI,同时性能保持在可接受范围内——它买的是可维护性,付的是性能。

列表 key 用数组索引。 只要列表会增删或排序,索引就会错位。React 会把「第 2 项」当成同一个节点复用,导致输入框内容跟着串位。用数据里稳定的 id。

以为 setState 是异步的。 更准确的说法是「批量合并的」。React 会把同一事件循环内的多次调用合并成一次渲染,所以调用后立刻读 state 拿到的是旧值。需要基于前值计算时,传函数而不是传对象。

给每个函数都包 useCallback useCallback 本身有成本(创建依赖数组、每次渲染做比较)。只有在「这个函数会作为 props 传给被 memo 包裹的子组件」或者「它是某个 effect 的依赖」时才有收益。无脑包裹只会增加复杂度和依赖项填错的风险。

项目一开始就上状态管理库。 先用 useStateContext,等到确实出现「多个远距离组件共享同一份可变状态」时再引入。提前引入 Redux 这类方案,收益没见到,样板代码先写了一堆。

本专题全部文章

  1. React 状态管理选型,2024年我的建议React 状态管理选型:Redux、MobX、Context、Zustand 各自适合什么场景。先回答「什么时候才真需要状态管理库」,再按项目规模与团队情况给具体建议,不推销单一方案。
  2. useCallback 和 useMemo 我之前一直用错了useCallback 与 useMemo 正确用法:这两个 Hook 极易被滥用。讲清它们各自缓存什么、什么情况下才真正有收益,以及无脑包裹反而带来的复杂度与依赖项 bug。
  3. React 类组件和函数组件区别React 类组件与函数组件的区别:写法、state 与生命周期的实现方式、this 指向、性能表现与心智模型上的差异,以及两者共同遵守的「不可修改自身 props」约束。
  4. React 父子组件交互生命周期执行顺序React 父子组件生命周期执行顺序:挂载、更新、卸载三个阶段父子钩子的交错顺序完整梳理,帮你判断某段逻辑该放在父组件还是子组件的哪个钩子里。
  5. React render propsReact Render Props 详解:用一个值为函数的 prop 在组件间共享代码的技术,它的实现方式、与高阶组件的取舍,以及在 Hooks 出现后的适用场景。
  6. React setState 深入:批量合并、异步表现与同步时机React setState 深入:为什么它表现得像异步、批量更新的合并规则、传函数与传对象的差异,以及在生命周期和原生事件回调中行为为何不同。
  7. React Refs 详解:访问 DOM 节点与向父组件暴露引用React Refs 详解:在典型数据流之外访问 DOM 节点或组件实例的方式,createRef 与 useRef 的用法、回调 ref、forwardRef 转发,以及什么时候不该用 ref。
  8. React Fiber 架构:增量渲染如何解决页面卡顿React Fiber 详解:核心算法的重写,目标是让渲染工作可切分成块并分散到多帧执行(增量渲染),从而改善动画、布局与手势场景下的响应性。
  9. React 虚拟 DomReact 虚拟 DOM 详解:在数据与真实 DOM 之间加一层缓冲,通过新旧虚拟 DOM 比对只把变化部分更新到真实 DOM,这就是 React 性能表现的来源与它的实际代价。
  10. React Diff 算法React Diff 算法与性能优化:传统 diff 的 O(n³) 复杂度问题,React 如何用同层比较、类型判断、key 标识三个策略把它降到 O(n),以及据此写出高性能列表的方法。
  11. React Hook 使用指南:两条规则与 useState、useEffect 实战React Hook 入门:Hook 是 React 16.8 的新增特性,让你不写 class 也能用 state 和其他 React 特性。讲清什么时候该用 Hook、以及它解决了类组件的哪些问题。
  12. React Hooks 全解:数据驱动、副作用与 React 18 新增 HookReact Hooks 详解:useState、useEffect、useRef、useContext、useReducer 等核心 Hook 的用法与执行时机,以及 Hooks 的使用规则和背后的实现约束。
  13. React 组件生命周期详解:新旧钩子对比与四个阶段React 组件生命周期详解:挂载、更新、卸载三个阶段各自会执行哪些方法、每个钩子适合做什么,以及 React 16 之后废弃的几个钩子和它们的替代方案。
  14. React 高阶组件 HOC:属性代理与反向继承两种实现React 高阶组件(HOC)详解:什么是高阶组件、怎么用它复用组件逻辑、属性代理与反向继承两种实现方式,以及 HOC 的命名约定和使用时的注意事项。