React性能优化分享

本篇将介绍在React编码过程中需要注意的性能优化点。鉴于图片懒加载、虚拟滚动列表等已成为广为人知的通用性能优化手段,本文将不再赘述这些内容。

  1. memo

    memo允许组件在 props 没有改变的情况下跳过重新渲染

    默认通过Object.is比较每个prop,可通过第二个参数,传入自定义函数来控制对比过程

    const Chart = memo(function Chart({ dataPoints }) {
      // ...
    }, arePropsEqual);
    
    function arePropsEqual(oldProps, newProps) {
      return (
        oldProps.dataPoints.length === newProps.dataPoints.length &&
        oldProps.dataPoints.every((oldPoint, index) => {
          const newPoint = newProps.dataPoints[index];
          return oldPoint.x === newPoint.x && oldPoint.y === newPoint.y;
        })
      );
    }
    
  2. useMemo

    在每次重新渲染的时候能够缓存计算的结果,只有传入的参数发生变化后,该计算函数才会重新调用计算新的结果。

    import { useState, useMemo } from "react";
    
    function App() {
      const [count, setCount] = useState(0);
    
      const memoizedValue = useMemo(() => {
        //创建1000位数组
        const list = new Array(1000).fill(null).map((_, i) => i);
    
        //对数组求和
        const total = list.reduce((res, cur) => (res += cur), 0);
    
        //返回计算的结果
        return count + total;
    
        //添加依赖项,只有count改变时,才会重新计算
      }, [count]);
    
      return (
        <div>
          {memoizedValue}
          <button onClick={() => setCount((prev) => prev + 1)}>按钮</button>
        </div>
      );
    }
    
    export default App;
    
  3. useCallback

    缓存函数的引用地址,仅在依赖项改变时才会更新。避免在每次渲染时都创建新的函数,useCallback 返回一个记忆化的回调函数,它只在其依赖项改变时才会改变

    import { useState, memo } from 'react';
    
    const App = () => {
      const [count, setCount] = useState(0);
    
      const handleClick = () => {
        setCount((prev) => prev + 1);
      };
    
      return (
        <div>
          {count}
          <MyButton handleClick={handleClick} />
        </div>
      );
    };
    
    const MyButton = memo(function MyButton({ handleClick }: { handleClick: () => void }) {
      console.log('子组件渲染');
      return <button onClick={handleClick}>按钮</button>;
    });
    
    export default App;
    

    点击按钮,可以发现即使子组件使用memo包裹了,但还是更新了,控制台打印出“子组件渲染”。这是因为父组件App每次更新时,函数handleClick每次都返回了新的引用地址,因此对于子组件来说每次传入的都是不一样的值,从而触发重渲染。

    同样的,减少使用通过内联函数绑定事件。每次父组件更新时,匿名函数都会返回一个新的引用地址,从而触发子组件的重渲染

    <MyButton handleClick={() => setCount((prev) => prev + 1)} />
    

    使用useCallback可以缓存函数的引用地址,将handleClick改为

    const handleClick = useCallback(()=>{  setCount(prev=>prev+1)},[])
    

    再点击按钮,会发现子组件不会再重新渲染。

  4. useTransition

    使用useTransition提供的startTransition来标记一个更新作为不紧急的更新。这段任务可以接受延迟或被打断渲染,进而去优先考虑更重要的任务执行

    页面会先显示list2的内容,之后再显示list1的内容

    import { useState, useEffect, useTransition } from "react";
    
    const App = () => {
      const [list1, setList1] = useState<null[]>([]);
      const [list2, setList2] = useState<null[]>([]);
      const [isPending, startTransition] = useTransition();
      useEffect(() => {
        startTransition(() => {
           //将状态更新标记为 transition  
          setList1(new Array(10000).fill(null));
        });
      }, []);
      useEffect(()=>{
        setList2(new Array(10000).fill(null));
      },[])
      return (
        <>
          {isPending ? "pending" : "nopending"}
          {list1.map((_, i) => (
            <div key={i}>{i}</div>
          ))}
          -----------------list2
          {list2.map((_, i) => (
            <div key={i}>6666</div>
          ))}
        </>
      );
    };
    
    export default App;
    
  5. useDeferredValue

    可以让我们延迟渲染不紧急的部分,类似于防抖但没有固定的延迟时间

    import { useState, useDeferredValue } from 'react';
    
    function SearchPage() {
      const [query, setQuery] = useState('');
      const deferredQuery = useDeferredValue(query);
      // ...
    }
    
  6. Fragment

    当呈现多个元素而不需要额外的容器元素时,使用React.Fragment可以减少DOM节点的数量,从而提高呈现性能

    const MyComponent = () => {
      return (
        <React.Fragment>
          <div>Element 1</div>
          <div>Element 2</div>
          <div>Element 3</div>
        </React.Fragment>
      );
    };
    
  7. 合理使用Context

    Context 能够在组件树间跨层级数据传递,正因其这一独特机制,Context 可以绕过 React.memo 或 shouldComponentUpdate 设定的比较过程。

    也就是说,一旦 Context 的 Value 变动,所有使用 useContext 获取该 Context 的组件会全部 forceUpdate。即使该组件使用了memo,且 Context 更新的部分 Value 与其无关

    为了使组件仅在 context 与其相关的value发生更改时重新渲染,将组件分为两个部分。在外层组件中从 context 中读取所需内容,并将其作为 props 传递给使用memo优化的子组件。

  8. 尽量避免使用index作为key

    在渲染元素列表时,尽量避免将数组索引作为组件的key。如果列表项有添加、删除及重新排序的操作,使用index作为key,可能会使节点复用率变低,进而影响性能

    使用数据源的id作为key

    const MyComponent = () => {
      const items = [{ id: 1, name: "Item 1" }, { id: 2, name: "Item 2" }, { id: 3, name: "Item 3" }];
    
      return (
          <ul>
            {items.map(item => (
              <li key={item.id}>{item.name}</li>
            ))}
          </ul>
        );
    };
    
  9. 懒加载

    通过React.lazy和React.Suspense实施代码分割策略,将React应用细分为更小的模块,确保在具体需求出现时才按需加载相应的部分

    定义路由

    import { lazy } from 'react';
    import { createBrowserRouter } from 'react-router-dom';
    
    const Login = lazy(() => import('../pages/login'));
    
    const routes = [
      {
        path: '/login',
        element: <Login />,
      },
    ];
    
    //可传第二个参数,配置base路径 { basename: "/app"}
    const router = createBrowserRouter(routes);
    
    export default router;
    

    引用路由

    import { Suspense } from 'react';
    import { RouterProvider } from 'react-router-dom';
    
    import ReactDOM from 'react-dom/client';
    
    import router from './router';
    
    const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
    
    root.render(
      <Suspense fallback={<div>Loading...</div>}>
        <RouterProvider router={router} />
      </Suspense>,
    );
    
  10. 组件卸载时的清理

    在组件卸载时清理全局监听器、定时器等。防止内存泄漏影响性能

    import { useState, useEffect, useRef } from 'react';
    
    function MyComponent() {
      const [count, setCount] = useState(0);
      const timer = useRef<NodeJS.Timeout>();
    
      useEffect(() => {
        // 定义定时器
        timer.current = setInterval(() => {
          setCount((count) => count + 1);
        }, 1000);
    
        const handleOnResize = () => {
          console.log('Window resized');
        };
    
        // 定义监听器
        window.addEventListener('resize', handleOnResize);
    
        // 在组件卸载时清除定时器和监听器
        return () => {
          clearInterval(timer.current);
          window.removeEventListener('resize', handleOnResize);
        };
      }, []);
    
      return (
        <div>
          <p>{count}</p>
        </div>
      );
    }
    
    export default MyComponent;
    
  11. Hooks

    使用React Hooks可以更方便的管理组件的状态和副作用,减少组件的复杂度和重复代码。

  12. 使用React.PureComponent , shouldComponentUpdate

    父组件状态的每次更新,都会导致子组件的重新渲染,即使是传入相同props。但是这里的重新渲染不是说会更新DOM,而是每次都会调用diif算法来判断是否需要更新DOM。这对于大型组件例如组件树来说是非常消耗性能的。
    在这里我们就可以使用React.PureComponent , shouldComponentUpdate生命周期来确保只有当组件props状态改变时才会重新渲染

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/713884.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

FPGA IO_BANK、IO_STANDARD

描述 Xilinx 7系列FPGA和UltraScale体系结构提供了高性能&#xff08;HP&#xff09;和 高范围&#xff08;HR&#xff09;I/O组。I/O库是I/O块&#xff08;IOB&#xff09;的集合&#xff0c;具有可配置的 SelectIO驱动程序和接收器&#xff0c;支持多种标准接口 单端和差分。…

vxe-table表格新增节点

做前端的朋友可以参考下&#xff1a;也可结合实际需求查看相应的官方文档 效果图 附上完整代码 <template><div><vxe-toolbar ref"toolbarRef" :refresh"{queryMethod: searchMethod}" export print custom><template #buttons>&…

React写一个 Modal组件

吐槽一波 最近公司的项目终于度过了混乱的前期开发&#xff0c;现在开始有了喘息时间可以进行"规范"的处理了。 组件的处理&#xff0c;永远是前端的第一大任务&#xff0c;尤其是在我们的ui库并不怎么可靠的情况下&#xff0c;各个组件的封装都很重要&#xff0c;而…

minium小程序自动化

一、安装minium pip install minium二、新建config.json {"dev_tool_path": "D:\\Program Files (x86)\\Tencent\\微信web开发者工具\\cli.bat","project_path": "小程序项目路径" }三、编写脚本 import miniumclass FirstTest(min…

【Echarts系列】平滑折线面积图

【Echarts系列】平滑折线面积图 序示例数据格式代码 序 为了节省后续开发学习成本&#xff0c;这个系列将记录我工作所用到的一些echarts图表。 示例 平滑折线面积图如图所示&#xff1a; 数据格式 data [{name: 2020年,value: 150},{name: 2021年,value: 168},{name: 2…

设计模式-装饰器模式Decorator(结构型)

装饰器模式(Decorator) 装饰器模式是一种结构模式&#xff0c;通过装饰器模式可以在不改变原有类结构的情况下向一个新对象添加新功能&#xff0c;是现有类的包装。 图解 角色 抽象组件&#xff1a;定义组件的抽象方法具体组件&#xff1a;实现组件的抽象方法抽象装饰器&…

git的ssh安装,windows通过rsa生成密钥认证问题解决

1 windows下载 官网下载可能出现下载太慢的情况&#xff0c;Git官网下载地址为&#xff1a;官网&#xff0c;推荐官网下载&#xff0c;如无法下载&#xff0c;可移步至CSDN&#xff0c;csdn下载地址&#xff1a;https://download.csdn.net/download/m0_46309087/12428308 2 Gi…

【Linux】程序地址空间之动态库的加载

我们先进行一个整体轮廓的了解&#xff0c;随后在深入理解细节。 在动态库加载之前还要说一下程序的加载&#xff0c;因为理解了程序的加载对动态库会有更深的理解。 轮廓&#xff1a; 首先&#xff0c;不管是程序还是动态库刚开始都是在磁盘中的&#xff0c;想要执行对应的可…

PHP在线生成查询产品防伪证书系统源码

源码介绍 PHP在线生成查询产品防伪证书系统源码&#xff0c;源码自带90套授权证书模板&#xff0c;带PSD公章模板&#xff0c;证书PSD源文件。 环境要求&#xff1a;PHPMYSQL&#xff0c;PHP 版本请使用PHP5.1 ~5.3。 图片截图 源码安装说明 1.上传所有文件至你的空间服务器…

学会python——显示进度条(python实例五)

目录 1、认识Python 2、环境与工具 2.1 python环境 2.2 Visual Studio Code编译 3、进度条显示 3.1 代码构思 3.2 代码示例 3.3 运行结果 4、总结 1、认识Python Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 Python 的设计具有很强的可读…

从零到爆款:用ChatGPT写出让人停不下来的短视频文案

一、前言 在自媒体的浪潮中&#xff0c;精彩的短视频文案对内容传播至关重要。众多辅助工具之中&#xff0c;凭借强大的语言处理能力和广泛的应用场景&#xff0c;ChatGPT成为了内容创作者的重要助力。接下来&#xff0c;我将介绍如何借助ChatGPT编写引人入胜的短视频文案&…

积木搭建游戏-第13届蓝桥杯省赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第83讲。 积木搭建游戏&…

Windows10 利用QT搭建SOEM开发环境

文章目录 一. SOEM库简介二. 安装WinPcap三. SOEM(1.4)库安装(1) 编译32位库(2) 编译64位库 四. 运行SOEM示例代码五. WIN10下利用QT构建SOEM开发环境 一. SOEM库简介 SOEM&#xff08;Scalable Open EtherCAT Master 或 Simple Open EtherCAT Master&#xff09;是一个开源的…

【OrangePiKunPengPro】 linux下编译、安装Boa服务器

OrangePiKunPengPro | linux下编译、安装Boa服务器 时间&#xff1a;2024年6月7日21:41:01 1.参考 1.boa- CSDN搜索 2.Boa服务器 | Ubuntu下编译、安装Boa_ubuntu安装boa-CSDN博客 3.i.MX6ULL—ElfBoard Elf1板卡 移植boa服务器的方法 (qq.com) 2.实践 2-1下载代码 [fly752fa…

python将数据保存到文件的多种实现方式

&#x1f308;所属专栏&#xff1a;【python】✨作者主页&#xff1a; Mr.Zwq✔️个人简介&#xff1a;一个正在努力学技术的Python领域创作者&#xff0c;擅长爬虫&#xff0c;逆向&#xff0c;全栈方向&#xff0c;专注基础和实战分享&#xff0c;欢迎咨询&#xff01; 您的…

EasyRecovery2024数据恢复神器#电脑必备良品

EasyRecovery数据恢复软件&#xff0c;让你的数据重见天日&#xff01; 大家好&#xff01;今天我要给大家种草一个非常实用的软件——EasyRecovery数据恢复软件&#xff01;你是不是也曾经遇到过不小心删除了重要的文件&#xff0c;或者电脑突然崩溃导致数据丢失的尴尬情况呢&…

手机照片免费数据恢复软件EasyRecovery2024免费版下载

大家好&#xff01;今天我要给大家推荐一款非常棒的软件——EasyRecovery。相信大家都知道&#xff0c;电脑中的重要文件一旦丢失&#xff0c;对我们的工作和学习都会产生很大的影响。 而EasyRecovery软件就是专门解决这个问题的利器&#xff01;它能够帮助我们快速、有效地恢…

第三篇—基于黑白样本的webshell检测

本篇为webshell检测的第三篇&#xff0c;主要讲的是基于黑白样本的webshell预测&#xff0c;从样本收集、特征提取、模型训练&#xff0c;最后模型评估这四步&#xff0c;实现一个简单的黑白样本预测模型。   若有误之处&#xff0c;望大佬们指出 Ⅰ 基本实现步骤 样本收集&…

Unity中的伽马(Gamma)空间和线性(Linear)空间

伽马空间定义&#xff1a;通常用于描述图像在存储和显示时的颜色空间。在伽马空间中&#xff0c;图像的保存通常经过伽马转换&#xff0c;使图片看起来更亮。 gamma并不是色彩空间&#xff0c;它其实只是如何对色彩进行采样的一种方式 为什么需要Gamma&#xff1a; 在游戏业…

53. QT插件开发--插件(动态库so)的调用与加载

1. 说明 在使用QT进行插件库的开发之后,还需要将这个插件库程序生成的so动态链接库加载到主程序框架中进行使用,才能达到主程序的模块化开发的效果。在前一篇文章插件创建中介绍了如何在QT中开发插件库,并提供外部接口调用。本篇博客的主要作用是模拟在主程序框架中加载动态…