【React】React 性能优化

一、React 更新流程(结合 React 18/19 底层原理)

React 在props 或者 state 发生改变时,会调用 React 的render 方法,创建一颗不同的树。

React 18 的更新流程基于 Fiber 架构并发模式(Concurrent Mode),核心分为三个阶段:

  1. 调度阶段(Scheduler)

    • 优先级调度 :通过 lane 模型管理任务优先级(如用户交互事件优先级高于数据请求),调度器(Scheduler)将任务分片为 5ms 的时间片。
    • 可中断性 :通过 shouldYield() 判断时间片是否用完,中断低优先级任务,确保主线程不阻塞。
  2. 协调阶段(Reconciler)

    • Fiber 树构建 :对比新旧虚拟 DOM(Diff 算法),生成副作用链表(如插入、更新、删除标记)。React 18 支持增量渲染,通过 workInProgress 双缓存树实现异步可中断的协调过程。
    • 并发更新:高优先级更新可中断低优先级任务,例如用户输入时暂停长列表渲染。
  3. 提交阶段(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 函数的调用机制

  1. 触发条件

  2. React 18 优化

    • 自动批处理 :事件处理函数中的多次 setState 合并为单次渲染。
    • 并发渲染:低优先级更新可能被中断,避免界面卡顿。
  3. 性能陷阱

    • 无意义渲染:未优化的父组件渲染会触发所有子组件重新协调(即使 Props 未变)。

四、性能优化手段对比

方法 适用场景 原理 注意事项
shouldComponentUpdate Class 组件 手动比较新旧 Props/State,返回 false 阻止渲染。 开发者工作量大。深比较可能性能更差,需权衡。
PureComponent(类组件继承PureComponent而不是Component) Class 组件 自动浅比较 Props/State,仅第一层属性变化时渲染。底层使用shallowEqual方法。 嵌套对象/数组变化可能漏检,需配合不可变数据。
React.memo 函数组件 类似 PureComponent,通过浅比较 Props 或自定义比较函数。 默认浅比较,复杂对象需自定义比较函数(如 (prev, next) => prev.id === next.id)。

五、底层原理与最佳实践

  1. 浅比较(shallowEqual)的局限性

    • 仅检查对象第一层属性的引用是否相同,深层嵌套数据变化无法触发更新。
    • 解决方案 :使用不可变数据(如 immer 库)或手动深比较(谨慎使用)或者直接赋值第一层一个新的引用对象(或者数组)。
  2. 高阶组件 Memo 的并发模式适配

    • React 18 的并发渲染中,memo 可减少非必要协调任务,提升高优先级更新的响应速度。
    • 自定义比较函数需轻量,避免抵消性能收益。
  3. 服务端组件(React 19)

    • 将静态部分(如布局)标记为服务端组件,减少客户端渲染负载。
    • 结合流式渲染(Streaming SSR),逐步注水提升首屏性能。
相关推荐
祈澈菇凉16 分钟前
如何结合使用thread-loader和cache-loader以获得最佳效果?
前端
垣宇18 分钟前
Vite 和 Webpack 的区别和选择
前端·webpack·node.js
java1234_小锋22 分钟前
一周学会Flask3 Python Web开发-客户端状态信息Cookie以及加密
前端·python·flask·flask3
化作繁星25 分钟前
如何在 React 中测试高阶组件?
前端·javascript·react.js
初遇你时动了情28 分钟前
react module.scss 避免全局冲突类似vue中scoped
vue.js·react.js·scss
Au_ust32 分钟前
千峰React:函数组件使用(2)
前端·javascript·react.js
爱吃南瓜的北瓜42 分钟前
npm install 卡在“sill idealTree buildDeps“
前端·npm·node.js
TTc_1 小时前
记录首次安装远古时代所需的运行环境成功npm install --save-dev node-sass
前端·npm·sass
翻滚吧键盘1 小时前
npm使用了代理,但是代理软件已经关闭导致创建失败
前端·npm·node.js
烂蜻蜓1 小时前
Uniapp 设计思路全分享
前端·css·vue.js·uni-app·html