当前位置: 首页 > article >正文

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.useMemo
缓存函数的引用地址,仅在依赖项改变时才会更新

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;

http://www.kler.cn/a/459536.html

相关文章:

  • 【踩坑指南:2025年最新】如何在Linux(Ubuntu)启动第一个Scala Hello World程序(Scala3)
  • 数据挖掘——神经网络分类
  • 腾讯云智能结构化 OCR:驱动多行业数字化转型的核心引擎
  • 人工智能之机器学习算法
  • I2C(一):存储器模式:stm32作为主机对AT24C02写读数据
  • GIT 企业级开发学习 1_基本操作
  • springboot511基于SpringBoot视频点播系统的设计与实现(论文+源码)_kaic
  • php 静态变量
  • linux 配置端口转发
  • 前端Python应用指南(四)Django实战:创建一个简单的博客系统
  • Java的SpringMVC
  • 嵌入式硬件杂谈(八)电源的“纹波”到底是什么?
  • Linux 信号集与信号掩码
  • uniapp小程序使用rich-text富文本图片溢出问题
  • ZYNQ7000双核AMP文档解读
  • 爬虫的工作原理
  • 百度热力图数据处理流程Arcgis PRO篇,Arcgis,QGIS见链接其他文章
  • 互联网全景消息(8)之RabbitMQ进阶介绍
  • 【机器学习】概述
  • 【C++11】类型分类、引用折叠、完美转发
  • 【数据库初阶】Linux中表的基础操作
  • 【Redis】集群配置(主从复制 哨兵搭建)
  • JPA查询部分字段的最佳实践
  • Python 中的 with open:文件操作的最佳实践
  • 发布远程组件vue2+Webpack和vue3+vite
  • 面试场景题系列:设计云盘系统