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中setState/useState的使用方法详细介绍

    React中setState/useState的使用方法详细介绍

    这篇文章主要介绍了React中setState/useState的使用方法,useState 和 setState 在React开发过程中 使用很频繁,但很多人都停留在简单的使用阶段,并没有正在了解它们的执行机制
    2023-04-04
  • Native Memory Tracking追踪区域示例分析

    Native Memory Tracking追踪区域示例分析

    这篇文章主要为大家介绍了Native Memory Tracking追踪区域示例分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • antd 3.x Table组件如何快速实现虚拟列表详析

    antd 3.x Table组件如何快速实现虚拟列表详析

    这篇文章主要给大家介绍了关于antd 3.x Table组件如何快速实现虚拟列表的相关资料,文中通过实例代码介绍的非常详细,对大家学习或者使用antd具有一定的参考学习价值,需要的朋友可以参考下
    2022-11-11
  • React组件学习之Hooks使用

    React组件学习之Hooks使用

    这篇文章主要介绍了React hooks组件通信,在开发中组件通信是React中的一个重要的知识点,本文通过实例代码给大家讲解react hooks中常用的父子、跨组件通信的方法,需要的朋友可以参考下
    2022-08-08
  • 在 React Native 中给第三方库打补丁的过程解析

    在 React Native 中给第三方库打补丁的过程解析

    这篇文章主要介绍了在 React Native 中给第三方库打补丁的过程解析,有时使用了某个React Native 第三方库,可是它有些问题,我们不得不修改它的源码,本文介绍如何修改源码又不会意外丢失修改结果的方法,需要的朋友可以参考下
    2022-08-08
  • antd之RangePicker设置默认值方式

    antd之RangePicker设置默认值方式

    这篇文章主要介绍了antd之RangePicker设置默认值方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-12-12
  • React通过hook实现封装表格常用功能

    React通过hook实现封装表格常用功能

    这篇文章主要为大家详细介绍了React通过hook封装表格常用功能的使用,文中的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以参考下
    2023-12-12
  • Redux thunk中间件及执行原理详细分析

    Redux thunk中间件及执行原理详细分析

    redux的核心概念其实很简单:将需要修改的state都存入到store里,发起一个action用来描述发生了什么,用reducers描述action如何改变state tree,这篇文章主要介绍了Redux thunk中间件及执行原理分析
    2022-09-09
  • react-router v4如何使用history控制路由跳转详解

    react-router v4如何使用history控制路由跳转详解

    这篇文章主要给大家介绍了关于react-router v4如何使用history控制路由跳转的相关资料,文中通过示例代码介绍的的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2018-01-01
  • 解决antd的Table组件使用rowSelection属性实现多选时遇到的bug

    解决antd的Table组件使用rowSelection属性实现多选时遇到的bug

    这篇文章主要介绍了解决antd的Table组件使用rowSelection属性实现多选时遇到的bug问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08

最新评论