一、React 更新流程(结合 React 18/19 底层原理)
React 在props 或者 state 发生改变时,会调用 React 的render 方法,创建一颗不同的树。
React 18 的更新流程基于 Fiber 架构 和 并发模式(Concurrent Mode),核心分为三个阶段:
-
调度阶段(Scheduler)
- 优先级调度 :通过
lane
模型管理任务优先级(如用户交互事件优先级高于数据请求),调度器(Scheduler)将任务分片为 5ms 的时间片。 - 可中断性 :通过
shouldYield()
判断时间片是否用完,中断低优先级任务,确保主线程不阻塞。
- 优先级调度 :通过
-
协调阶段(Reconciler)
- Fiber 树构建 :对比新旧虚拟 DOM(Diff 算法),生成副作用链表(如插入、更新、删除标记)。React 18 支持增量渲染,通过
workInProgress
双缓存树实现异步可中断的协调过程。 - 并发更新:高优先级更新可中断低优先级任务,例如用户输入时暂停长列表渲染。
- Fiber 树构建 :对比新旧虚拟 DOM(Diff 算法),生成副作用链表(如插入、更新、删除标记)。React 18 支持增量渲染,通过
-
提交阶段(Renderer)
- 同步 DOM 更新:根据副作用链表批量更新真实 DOM,此阶段不可中断。
- 自动批处理 :React 18 对事件回调、Promise 等场景的多个
setState
自动合并为单次渲染。
React 19 改进:
- 引入更细粒度的 服务端组件(Server Components),支持流式渲染和选择性注水(Hydration)。
- 优化错误边界处理,提供
onCaughtError
等 API 自定义错误上报。
二、Keys 的优化原理
Key 的作用:
- 唯一标识:帮助 React 在 Diff 过程中识别元素身份,减少不必要的 DOM 操作。
- 状态保留:相同 Key 的组件实例会被复用,避免内部状态(如表单输入)丢失。
优化实践:
- 避免索引作为 Key:动态列表中使用索引会导致元素错位(如删除中间项),而且对性能没有优化。
- 唯一稳定值 :优先使用数据 ID(如
item.id
),若无则用哈希值(如item.name + timestamp
)。 - 不要使用随机数:随机数在下一次 render 时,会重新生成一个数字。
底层机制:
- Diff 算法 :通过 Key 快速匹配新旧元素,仅对移动、新增、删除的节点操作 DOM,复杂度从 O(n³) 降至 O(n)。
- 同层节点之间相互比较,不会垮节点比较;
- 不同类型的节点,产生不同的树结构;
- 开发中,可以通过key来指定哪些节点在不同的渲染下保持稳定
在最后插入数据有无key 意义不大。前面插入数据如果没有 key 后面的节点都需要修改,如果有key ,原节点只需要移动,不需要修改。
三、Render 函数的调用机制
-
触发条件:
-
State/Props 变更 :父组件渲染(父组件render 调用,所有子组件的render 都会重新调用)、
setState
、Context 变化等。 -
Force Update :
forceUpdate()
跳过shouldComponentUpdate
(SCU) 生命周期,进行强制渲染。
-
-
React 18 优化:
- 自动批处理 :事件处理函数中的多次
setState
合并为单次渲染。 - 并发渲染:低优先级更新可能被中断,避免界面卡顿。
- 自动批处理 :事件处理函数中的多次
-
性能陷阱:
- 无意义渲染:未优化的父组件渲染会触发所有子组件重新协调(即使 Props 未变)。
四、性能优化手段对比
方法 | 适用场景 | 原理 | 注意事项 |
---|---|---|---|
shouldComponentUpdate | Class 组件 | 手动比较新旧 Props/State,返回 false 阻止渲染。 |
开发者工作量大。深比较可能性能更差,需权衡。 |
PureComponent(类组件继承PureComponent而不是Component) | Class 组件 | 自动浅比较 Props/State,仅第一层属性变化时渲染。底层使用shallowEqual方法。 | 嵌套对象/数组变化可能漏检,需配合不可变数据。 |
React.memo | 函数组件 | 类似 PureComponent ,通过浅比较 Props 或自定义比较函数。 |
默认浅比较,复杂对象需自定义比较函数(如 (prev, next) => prev.id === next.id )。 |
五、底层原理与最佳实践
-
浅比较(shallowEqual)的局限性:
- 仅检查对象第一层属性的引用是否相同,深层嵌套数据变化无法触发更新。
- 解决方案 :使用不可变数据(如
immer
库)或手动深比较(谨慎使用)或者直接赋值第一层一个新的引用对象(或者数组)。
-
高阶组件 Memo 的并发模式适配:
- React 18 的并发渲染中,
memo
可减少非必要协调任务,提升高优先级更新的响应速度。 - 自定义比较函数需轻量,避免抵消性能收益。
- React 18 的并发渲染中,
-
服务端组件(React 19):
- 将静态部分(如布局)标记为服务端组件,减少客户端渲染负载。
- 结合流式渲染(Streaming SSR),逐步注水提升首屏性能。