【react】常见的性能优化 1

目录

[常见的 React 性能优化手段](#常见的 React 性能优化手段)

[1. 使用 useMemo 和 useCallback 缓存数据和函数](#1. 使用 useMemo 和 useCallback 缓存数据和函数)

[2. 使用 React.memo 缓存组件](#2. 使用 React.memo 缓存组件)

[3. 组件懒加载](#3. 组件懒加载)

[4. 合理使用 key](#4. 合理使用 key)

[5. 在组件销毁时清除定时器/事件](#5. 在组件销毁时清除定时器/事件)

[6. 使用 Suspense 和 Lazy 拆分组件](#6. 使用 Suspense 和 Lazy 拆分组件)

[7. 使用 Fragment 避免额外标记](#7. 使用 Fragment 避免额外标记)

[8. 避免使用内联函数](#8. 避免使用内联函数)

[9. 避免使用内联样式](#9. 避免使用内联样式)

[10. 优化渲染条件](#10. 优化渲染条件)

[11. 为组件创建错误边界](#11. 为组件创建错误边界)

[12. 组件卸载前进行清理操作](#12. 组件卸载前进行清理操作)

[13. 使用 PureComponent](#13. 使用 PureComponent)

[14. 使用 shouldComponentUpdate](#14. 使用 shouldComponentUpdate)

[15. 在构造函数中进行函数 this 绑定](#15. 在构造函数中进行函数 this 绑定)

[16. 类组件中的箭头函数](#16. 类组件中的箭头函数)

[17. 避免数据结构突变](#17. 避免数据结构突变)

[18. 依赖优化](#18. 依赖优化)

常见的 React 性能优化手段

在开发 React 应用时,性能优化是确保应用高效运行的关键。以下是常见的 React 性能优化手段,并附带代码示例和解释。

1. 使用 useMemouseCallback 缓存数据和函数

useMemouseCallback 是 React 提供的 Hooks,用于缓存计算结果和函数引用,避免不必要的重新渲染。

示例:

javascript 复制代码
import React, { useState, useMemo, useCallback } from 'react';

const data = {
  userName: '张三',
  age: 19,
  fav: '篮球、排球',
};

const getUserInfo = () => {
  return {
    ...data,
    random: Math.random(),
  };
};

function Case2() {
  const [count, setCount] = useState(0);

  // 使用 useMemo 缓存数据
  const userInfo = useMemo(() => getUserInfo(), []);

  // 使用 useCallback 缓存函数
  const handleClick = useCallback(() => {
    setCount(count + 1);
  }, [count]);

  return (
    <div>
      <div>姓名:{userInfo.userName}</div>
      <div>年龄:{userInfo.age}</div>
      <div>爱好:{userInfo.fav}</div>
      <div>随机数: {userInfo.random}</div>
      <div>当前页面渲染次数: {count}</div>
      <button onClick={handleClick}>刷新渲染组件</button>
    </div>
  );
}

解释: useMemo 确保 getUserInfo 的结果只在首次渲染时计算一次。

useCallback 确保 handleClick 函数的引用不会在每次渲染时都重新创建。

2. 使用 React.memo 缓存组件

React.memo 类似于 shouldComponentUpdate,当 props 没有变化时,不会重新渲染组件,从而提高性能。

示例:

javascript 复制代码
import React from 'react';
import { Button } from 'antd';

const BasicButton = (props) => {
  return <Button {...props}></Button>;
};

export default React.memo(BasicButton, (oldProps, newProps) => {
  return oldProps === newProps; // true - 不更新 false - 更新
});

解释: React.memo 接收一个比较函数,只有当 props 发生变化时才会重新渲染组件。

3. 组件懒加载

使用组件懒加载可以减少初始 bundle 文件大小,加快组件加载速度。

示例:

javascript 复制代码
import React, { lazy, Suspense } from 'react';
import { BrowserRouter, Link, Route, Switch } from 'react-router-dom';

const Home = lazy(() => import(/* webpackChunkName: "Home" */ './Home'));
const List = lazy(() => import(/* webpackChunkName: "List" */ './List'));

function App() {
  return (
    <BrowserRouter>
      <Link to="/">Home</Link>
      <Link to="/list">List</Link>
      <Switch>
        <Suspense fallback={<div>Loading</div>}>
          <Route path="/" component={Home} exact />
          <Route path="/list" component={List} />
        </Suspense>
      </Switch>
    </BrowserRouter>
  );
}

export default App;

解释: lazy 动态导入组件,Suspense 提供加载状态。

4. 合理使用 key

map 循环中尽量使用唯一的标识作为 key,避免使用 index 作为 key,方便复用组件。

示例:

javascript 复制代码
import React from 'react';

const list = [
  { id: 1, name: '张三' },
  { id: 2, name: '李四' },
  { id: 3, name: '王五' },
];

function Case3() {
  return (
    <div>
      {list.map((item) => (
        <div key={item.id}>{item.name}</div>
      ))}
    </div>
  );
}

export default Case3;

解释: 使用 id 作为 key,确保每个元素的唯一性。

5. 在组件销毁时清除定时器/事件

组件卸载时清除相关事件、定时器,防止内存泄漏。

示例:

javascript 复制代码
import React, { useEffect, useState } from 'react';

function Case1() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const timer = setInterval(() => {
      setCount(count + 1);
    }, 1000);

    return () => {
      clearInterval(timer);
    };
  }, [count]);

  return <div>{count}</div>;
}

export default Case1;

解释: useEffect 返回的清理函数会在组件卸载时执行,清除定时器。

6. 使用 SuspenseLazy 拆分组件

通过 SuspenseLazy 实现按需加载组件,提升首屏加载速度。

示例:

javascript 复制代码
import React, { lazy } from 'react';
import ReactDOM from 'react-dom/client';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';

const LearnReactOptimize = lazy(() => import('./pages/LearnReactOptimize'));

const LazyBoundary = (WrapComp) => (
  <Suspense fallback={<div>loading....</div>}>
    <WrapComp />
  </Suspense>
);

const routeConfig = createBrowserRouter([
  {
    path: '/LearnReactOptimize',
    element: LazyBoundary(LearnReactOptimize),
  },
  {
    path: '/',
    element: <App />,
  },
]);

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <RouterProvider router={routeConfig} />
  </React.StrictMode>,
);

解释: Suspense 提供加载状态,Lazy 动态导入组件。

7. 使用 Fragment 避免额外标记

通过 Fragment 减少不必要的标签,简化 DOM 结构。

示例:

javascript 复制代码
import React from 'react';

// bad
function AppBad() {
  return (
    <div>
      <div>1</div>
      <div>2</div>
    </div>
  );
}

// good
function AppGood() {
  return (
    <>
      <div>1</div>
      <div>2</div>
    </>
  );
}

解释: Fragment (<>...</>) 不会生成额外的 DOM 元素。

8. 避免使用内联函数

避免在 JSX 中使用内联函数,以减少不必要的函数创建。

示例:

javascript 复制代码
import React from 'react';

// bad
function AppBad() {
  const handleClick = () => {
    console.log('click');
  };

  return <div onClick={() => handleClick()}>App</div>;
}

// good
function AppGood() {
  const handleClick = () => {
    console.log('click');
  };

  return <div onClick={handleClick}>App</div>;
}

**解释:**内联函数每次渲染都会创建新的函数实例,导致不必要的性能开销。

9. 避免使用内联样式

避免使用内联样式,将样式提取到 CSS 文件中,减少 JavaScript 执行时间。

示例:

javascript 复制代码
import React from 'react';
import './App.css'; // 引入外部 CSS 文件

function App() {
  return <div className="app-style">App works</div>;
}

export default App;

**解释:**将样式提取到外部 CSS 文件中,减少 JavaScript 执行时间。

10. 优化渲染条件

避免不必要的渲染,确保组件只在必要时更新。

示例:

javascript 复制代码
import React, { useState, useEffect } from 'react';

function App() {
  const [count, setCount] = useState(0);
  const [name] = useState("张三");

  useEffect(() => {
    setInterval(() => {
      setCount(prev => prev + 1);
    }, 1000);
  }, []);

  return (
    <div>
      {count}
      <ShowNameMemo name={name} />
    </div>
  );
}

function ShowName({ name }) {
  console.log("showName render...");
  return <div>{name}</div>;
}

const ShowNameMemo = React.memo(ShowName);

export default App;

解释: 使用 React.memo 防止不必要的重新渲染。

11. 为组件创建错误边界

捕获子组件中的错误,防止整个组件树崩溃。

示例:

javascript 复制代码
import React from 'react';

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    console.log(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children;
  }
}

export default ErrorBoundary;

解释: ErrorBoundary 捕获子组件中的错误,显示降级 UI。

12. 组件卸载前进行清理操作

确保组件卸载时清理定时器、事件监听等资源。

示例:

javascript 复制代码
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';

const App = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    let timer = setInterval(() => {
      setCount(prev => prev + 1);
    }, 1000);

    return () => {
      clearInterval(timer);
    };
  }, []);

  return (
    <button onClick={() => ReactDOM.unmountComponentAtNode(document.getElementById('root'))}>
      {count}
    </button>
  );
};

export default App;

解释: useEffect 返回的清理函数会在组件卸载时执行,清除定时器。

13. 使用 PureComponent

PureComponent 是类组件的优化版本,自动实现浅比较,减少不必要的渲染。

示例:

javascript 复制代码
import React from 'react';

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 1,
    };
  }

  componentDidMount() {
    this.setState({ count: 1 });
  }

  render() {
    return (
      <div>
        <RegularChildComponent count={this.state.count} />
        <PureChildComponent count={this.state.count} />
      </div>
    );
  }
}

class RegularChildComponent extends React.Component {
  render() {
    console.log('RegularChildComponent render');
    return <div>{this.props.count}</div>;
  }
}

class PureChildComponent extends React.PureComponent {
  render() {
    console.log('PureChildComponent render');
    return <div>{this.props.count}</div>;
  }
}

export default App;

解释: PureComponent 自动实现浅比较,减少不必要的渲染。

14. 使用 shouldComponentUpdate

手动控制组件是否需要更新。

示例:

javascript 复制代码
import React from 'react';

export default class App extends React.Component {
  constructor() {
    super();
    this.state = { name: '张三', age: 20, job: 'waiter' };
  }

  componentDidMount() {
    setTimeout(() => this.setState({ job: 'chef' }), 1000);
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (this.state.name !== nextState.name || this.state.age !== nextState.age) {
      return true;
    }
    return false;
  }

  render() {
    console.log('rendering');
    let { name, age } = this.state;
    return <div>{name} {age}</div>;
  }
}

解释: shouldComponentUpdate 控制组件是否需要更新。

15. 在构造函数中进行函数 this 绑定

确保类方法中的 this 指向正确。

示例:

javascript 复制代码
import React from 'react';

export default class App extends React.Component {
  constructor() {
    super();
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    console.log(this);
  }

  render() {
    return <button onClick={this.handleClick}>按钮</button>;
  }
}

解释: 构造函数中绑定 this,确保方法中的 this 指向正确。

16. 类组件中的箭头函数

使用箭头函数避免 this 绑定问题。

示例:

javascript 复制代码
import React from 'react';

export default class App extends React.Component {
  handleClick = () => console.log(this);

  render() {
    return <button onClick={this.handleClick}>按钮</button>;
  }
}

解释: 箭头函数自动绑定 this,避免手动绑定。

17. 避免数据结构突变

保持组件中 propsstate 的数据结构一致,避免突变。

示例:

javascript 复制代码
import React, { Component } from 'react';

export default class App extends Component {
  constructor() {
    super();
    this.state = {
      employee: {
        name: '张三',
        age: 20,
      },
    };
  }

  render() {
    const { name, age } = this.state.employee;
    return (
      <div>
        {name}
        {age}
        <button
          onClick={() =>
            this.setState({
              ...this.state,
              employee: {
                ...this.state.employee,
                age: 30,
              },
            })
          }
        >
          change age
        </button>
      </div>
    );
  }
}

**解释:**使用扩展运算符避免直接修改对象。

18. 依赖优化

优化第三方库的引入,减少打包体积。

示例:

  1. 安装依赖:

    bash 复制代码
    yarn add react-app-rewired customize-cra lodash babel-plugin-lodash
  2. 创建 config-overrides.js

    javascript 复制代码
    const { override, useBabelRc } = require('customize-cra');
    
    module.exports = override(useBabelRc());
  3. 修改 package.json

    javascript 复制代码
    "scripts": {
      "start": "react-app-rewired start",
      "build": "react-app-rewired build",
      "test": "react-app-rewired test --env=jsdom",
      "eject": "react-scripts eject"
    }
  4. 创建 .babelrc

    javascript 复制代码
    {
      "plugins": ["lodash"]
    }
  5. 使用 Lodash:

    javascript 复制代码
    import React from 'react';
    import _ from 'lodash';
    
    function App() {
      console.log(_.chunk(['a', 'b', 'c', 'd'], 2));
      return <div>App works</div>;
    }
    
    export default App;

解释 :使用 react-app-rewiredcustomize-cra 覆盖默认配置,babel-plugin-lodash 优化 Lodash 的引入。

码字不易,大佬们点点赞

相关推荐
计算机相关知识分享14 分钟前
Web前端基础知识(五)
前端
蜗牛_snail20 分钟前
Ant Design Vue 之可定位对话框
前端·javascript·vue.js
极客代码27 分钟前
深入理解C语言:编译原理
c语言·开发语言·性能优化·编译原理·代码优化
萧寂17327 分钟前
vue2使用tailwindcss
前端
明月看潮生43 分钟前
青少年编程与数学 02-006 前端开发框架VUE 04课题、组合式API
前端·javascript·vue.js·青少年编程·编程与数学
她和夏天一样热44 分钟前
【前端系列】Pinia状态管理库
前端·axios·pinia
小彭努力中1 小时前
57.在 Vue 3 中使用 OpenLayers 点击选择 Feature 设置特定颜色
前端·javascript·vue.js·arcgis·openlayers
JINGWHALE11 小时前
设计模式 结构型 适配器模式(Adapter Pattern)与 常见技术框架应用 解析
前端·人工智能·后端·设计模式·性能优化·系统架构·适配器模式
DX_水位流量监测1 小时前
水库水雨情监测系统:水位、雨量、流量等参数全天候实时监测
大数据·开发语言·前端·网络·人工智能·信息可视化
autumn8681 小时前
为什么最好吧css的link标签放在head之间?
前端