React渲染的优化方案

 更新时间:2024年08月14日 08:48:11   作者:brandonxiang  
react的渲染机制是非常独特的,有别于 Vue 框架的渲染次数的优化计算,React 很久以来就有PureComponent、shouldUpdate,本文小编给大家介绍了React渲染的优化方案,需要的朋友可以参考下

一、引子

react的渲染机制是非常独特的,有别于 Vue 框架的渲染次数的优化计算。React 很久以来就有PureComponent、shouldUpdate。Function component 又有了memo、useMemo、useCallback 这样的函数工具,让它成为有一定深度的前端框架。

怎么使用 useMemo 和 useCallback 是我们值得思考的点。

二、代码范式

首先,假设大家对 React 都有一个基础的入门水平,所以本文不再赘述“useMemo 和 useCallback ”基本用法。

2.1 Memo 缓存组件

引起重渲染的最常见的情况是,组件的 props。memo包裹着函数组件,针对props 参数的浅对比。

子组件的渲染有些情况,子组件控制不住,它受到父组件的参数影响。

function _Boxes({ boxes }: {
  boxes: {
    flex: number;
    background: string;
  }[]
}) {
  return (
    <div className="boxes-wrapper">
      {boxes.map((boxStyles, index) => (
        <div className="box" style={boxStyles} key={index} />
      ))}
    </div>
  );
}

const Boxes = memo(_Boxes);

2.2 父组件层面,对象使用 useMemo

作为 Box 的父组件,boxes 的传参是一个对象,每次改变,它会生成一个全新的对象。这里要对 boxes 对象使用缓存(useMemo)。这样,age 的改动将不会影响到 Box 的重渲染。

function App() {
  const [age, setAge] = React.useState(0);
  const [boxWidth, setBoxWidth] = React.useState(1);

  const id = React.useId();

  // Age 属性的变更不会影响 boxes 属性变化
  const boxes = useMemo(() => {
    return [
      { flex: boxWidth, background: 'hsl(345deg 100% 50%)' },
      { flex: 3, background: 'hsl(260deg 100% 40%)' },
      { flex: 1, background: 'hsl(50deg 100% 60%)' },
    ];
  }, [boxWidth]);

  return (
    <>
      <Boxes boxes={boxes} />

      <section>
        <button onClick={() => {
          setAge(age + 1)
        }}>
          Increment age
        </button>
        <p>Hello! You are {age}.</p>
      </section>

      <section>
        <label htmlFor={`${id}-box-width`}>
          First box width:
        </label>
        <input
          id={`${id}-box-width`}
          type="range"
          min={1}
          max={5}
          step={0.01}
          value={boxWidth}
          onChange={(event) => {
            setBoxWidth(Number(event.target.value));
          }}
        />
      </section>
    </>
  );
}

2.3 context provider 最好用 useMemo

同理,参考《How To useContext With useReducer》,context provider 的 value 参数是一个很容易被遗忘的点,provider 可能会传入一个对象,利用useMemo 或者 useCallback 来保护 App 组件不会做出过多重渲染。

const Main = () => {
  const [state, dispatch] = useReducer(reducer, { age: 42 });
  
  // 利用useMemo 或者 useCallback 来保护 Context 不会做出过多重渲染
  const contextValue = useMemo(() => {
    return { state, dispatch };
  }, [state, dispatch]);
  
  return (
    <MyContext.Provider value={contextValue}>
      <App />
    </MyContext.Provider>
  )
}

2.4 其他

还有一种特殊情况是:不规范、分批的 Context 调用导致了页面的重新渲染。针对一些老旧项目,以前的业务逻辑导致 Context 的调用混乱,已经不是前面几种方法能够解决的。

解决方法:改动代码,把多次 Context 调用整合为一次。

三、补救防范

3.1 断点查看调用堆栈

利用 Chrome 原生调试工具打断点,看每一行代码的堆栈信息。这种方式最为原始但是它往往“行之有效”。

3.2 devtool 查看渲染次数和渲染堆栈

React Devtool 的 Profiler 能协助我们排查 React 渲染次数和渲染堆栈。

  • 点击 Profiler 的记录圆圈
  • 刷新页面或者做其他操作
  • 停止记录
  • 参考快照记录

同时,我们还能够通过“highlight updates when components render”来可视化整个渲染过程。

3.3 渲染打印工具

ahooks的useWhyDidYouUpdate

该函数能够帮助开发者排查是哪个属性改变导致了组件的 rerender,但是更多集中在 props、state,开发者需要主动去缩小范围,它起到辅助打印的工作,如果是 Context 或者更外侧的数据变动,效果不见得达到效果。

Welldone Software 的 why-did-you-render

该工具能全局打印 rerender 日志,但是在复杂项目当中,它的打印较为混乱,不一定能够很好的发现问题。

四、总结

React 的渲染优化有非常多篇博客已经聊过,但是还是“No Silver Bullet”,没有最佳方案。特别在一些数据流非常复杂的前端工程项目当中。

React 的前端项目能够划分为:聪明组件和懒惰组件。聪明组件负责的内容是页面逻辑的数据流向,懒惰组件负责的是样式的渲染,数据流越是清晰,代码可维护性越强。

以下是我个人的代码编写建议:

  • 不要把所有数据都往 Context 里面放,只有极其核心,多个页面都复用情况。
  • 简单的页面,尽量以聪明组件和懒惰组件的方式来处理。
  • 如果已经出现数据流混乱的情况下,合理使用 memo,或者状态管理工具(例如zustand、jotai等)让代码数据流尽量走向清晰,初始化主路径尽量批量渲染好。

虽然有很多分析工具,但是它们更多是辅助,最重要还是要通过人工分析。重点要分析出渲染次数过多的代码,做出针对性地处理。

以上就是React渲染的优化方案的详细内容,更多关于React渲染优化的资料请关注脚本之家其它相关文章!

相关文章

  • react自适应布局px转rem实现示例详解

    react自适应布局px转rem实现示例详解

    这篇文章主要为大家介绍了react自适应布局px转rem实现示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • 浅谈React组件之性能优化

    浅谈React组件之性能优化

    这篇文章主要介绍了浅谈React组件之性能优化,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-03-03
  • 浅谈React之状态(State)

    浅谈React之状态(State)

    这篇文章主要介绍了浅谈React之状态(State),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-09-09
  • React onBlur回调中使用document.activeElement返回body完美解决方案

    React onBlur回调中使用document.activeElement返回body完美解决方案

    这篇文章主要介绍了React onBlur回调中使用document.activeElement返回body完美解决方案,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-04-04
  • React组件通信的实现示例

    React组件通信的实现示例

    在React中,组件通信是一个重要的概念,它允许不同组件之间进行数据传递和交互,本文主要介绍了React组件通信的实现示例,感兴趣的可以了解一下
    2023-11-11
  • React使用有限状态机的实现示例

    React使用有限状态机的实现示例

    本文主要介绍了React使用有限状态机的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-05-05
  • 一文带你搞懂React中的useReducer

    一文带你搞懂React中的useReducer

    useReducer 是除useState之外另一个与状态管理相关的 hook,这篇文章主要为大家介绍了useReducer应用的相关知识,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-06-06
  • React组件对子组件children进行加强的方法

    React组件对子组件children进行加强的方法

    这篇文章主要给大家介绍了关于React组件中对子组件children进行加强的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用React具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-06-06
  • React Refs转发实现流程详解

    React Refs转发实现流程详解

    Refs是一个 获取 DOM节点或React元素实例的工具,在React中Refs 提供了一种方式,允许用户访问DOM 节点或者在render方法中创建的React元素,这篇文章主要给大家介绍了关于React中refs的一些常见用法,需要的朋友可以参考下
    2022-12-12
  • 一文详解React Redux设计思想与工作原理

    一文详解React Redux设计思想与工作原理

    最近看项目中使用了 Redux, 便尝试了解一波 Redux 的设计思想与工作原理,所以本文详细的给大家介绍了Redux设计思想与工作原理,需要的朋友可以参考下
    2023-09-09

最新评论