React如何进行性能优化-方式、原理、示例

目录

一、使用shouldComponentUpdate和React.memo

1. 使用shouldComponentUpdate(类组件)

2.使用React.memo(函数组件)

二、使用useMemo

三、懒加载

四、避免使用匿名函数

五、避免使用内联对象

六、列表使用key属性


一、使用shouldComponentUpdate和React.memo

1. 使用shouldComponentUpdate(类组件)

 shouldComponentUpdate 是React生命周期渲染阶段的方法,用于在组件接收新的 props 或 state 时,决定组件是否需要重新渲染

原理:

如果组件的 props 或 state 发生变化,React 默认会重新渲染该组件及其子组件。然而,在很多情况下,即使 props 或 state 发生了变化,组件的输出也可能没有变化(即渲染结果相同)。这种情况下,重新渲染组件就是不必要的,会浪费资源。

 shouldComponentUpdate 方法允许我们在组件实际渲染之前进行自定义的条件判断。如果该方法返回 false,则 React 会跳过该组件的渲染过程及其子组件的渲染过程,从而避免不必要的 DOM 操作。

使用方法示例:

以下为TodoItem 组件,它接收 todo 和 completed 作为 props。我们只想在 todo 的内容发生变化时重新渲染组件,而忽略 completed 的变化

class TodoItem extends React.Component {  
  shouldComponentUpdate(nextProps, nextState) {  
    // 比较新旧 props 中的 todo 内容是否相同  
    // 注意:这里简化了比较,实际中可能需要深比较(特别是当 todo 是对象或数组时)
    //通过比较当前 props (this.props) 和下一个 props (nextProps) 中的 todo 属性来决定是否需要重新渲染  
    return this.props.todo !== nextProps.todo;  
  }  
  
  render() {  
    const { todo, completed } = this.props;  
    return (  
      <li style={{ textDecoration: completed ? 'line-through' : 'none' }}>  
        {todo}  
      </li>  
    );  
  }  
}

2.使用React.memo(函数组件)

React.memo 是 React 提供的一个高阶组件(HOC),用于对函数组件进行性能优化。它通过记忆(memoization)组件的渲染结果来避免在 props 没有变化时的重复渲染。

 原理:

基于浅比较(shallow comparison)来检查 props 是否发生变化,如果 props 没有变化,则直接返回上次渲染的结果,而不是重新渲染组件。

React.memo 允许传递一个可选的第二个参数,这是一个自定义的比较函数,用于确定 props 是否相等。如果提供了这个比较函数,React 会使用这个函数来进行 props 的比较,而不是默认的浅比较。

使用方法示例:

import React from 'react';  
  
// 原始组件  
function Counter({ count }) {  
  console.log('Counter is rendering...');  
  return <div>{count}</div>;  
}  
  
// 使用 React.memo 优化后的组件  
const MemoizedCounter = React.memo(Counter, (prevProps, nextProps) => {  
  // 这里使用浅比较作为示例,但在这个简单的例子中,默认比较就足够了  
  // 实际上,对于基本数据类型(如本例中的 count),默认比较就足够了  
  return prevProps.count === nextProps.count;  
});  
  
// 父组件,用于演示 Counter 组件的渲染  
function App() {  
  const [count, setCount] = React.useState(0);  
  
  // 注意:这里的 setCount 调用实际上会导致整个 App 组件重新渲染,  
  // 但由于 MemoizedCounter 被 memo 化了,只有当 count 变化时它才会重新渲染  
  const increment = () => setCount(count + 1);  
  
  return (  
    <div>  
      <MemoizedCounter count={count} />  
      <button onClick={increment}>Increment</button>  
    </div>  
  );  
}  
  
export default App;

二、使用useMemo

useMemo是React提供的一个自定义Hook,用于在渲染过程中执行一些昂贵的计算,并且仅在依赖项发生变化时重新计算,从而优化性能。

 原理:

useMemo接受一个函数和一个依赖项数组。在组件渲染过程中,useMemo会执行传入的函数,并返回函数的计算结果。

同时,它会监视依赖项数组中的值,只有当依赖项发生变化时,才会重新计算函数的返回值。这样可以避免不必要的计算,提高性能。

使用方法示例:

import React, { useState, useMemo } from 'react';  
  
function SumCalculator() {  
  const [number1, setNumber1] = useState(0);  
  const [number2, setNumber2] = useState(0);  
  
  // 使用useMemo进行记忆化计算  
  // 每当number1或number2发生变化时,useMemo会重新执行计算函数,计算两个数的和
  const sum = useMemo(() => {  
    console.log('Calculating sum...');  
    return number1 + number2;  
  }, [number1, number2]);  
  
  return (  
    <div>  
      <input  
        type="number"  
        value={number1}  
        onChange={(e) => setNumber1(parseInt(e.target.value))}  
      />  
      <input  
        type="number"  
        value={number2}  
        onChange={(e) => setNumber2(parseInt(e.target.value))}  
      />  
      <p>The sum is: {sum}</p>  
    </div>  
  );  
}  
  
export default SumCalculator;

三、懒加载

懒加载允许开发者延迟加载组件或资源,直到它们真正被需要时才进行加载。

 原理:

懒加载的原理基于动态导入(Dynamic Import),它允许开发者将组件或模块的代码分割成多个块,并在需要时按需加载。这种方式可以减少初始加载时间,因为用户只需要加载当前页面所需的代码,而不需要加载整个应用程序的所有代码。

在React中,懒加载通常与React.lazySuspense组件一起使用。React.lazy函数接受一个返回Promise对象的函数作为参数,该Promise对象解析为需要动态加载的React组件。而Suspense组件则用于包裹懒加载的组件,并提供一个加载指示器(如“Loading...”文本或加载动画),直到懒加载的组件加载完成。

示例:

import React, { Suspense, lazy } from 'react';  
  
// 使用React.lazy动态导入需要被懒加载的MyComponent组件  
const MyComponent = lazy(() => import('./MyComponent'));  
  
function App() {  
//当App组件渲染时,如果MyComponent尚未加载,Suspense组件将显示一个加载指示器(在这个例子中是“Loading...”文本)。一旦MyComponent加载完成,它将替换加载指示器并正常渲染。
  return (  
    <Suspense fallback={<div>Loading...</div>}>  
      <MyComponent />  
    </Suspense>  
  );  
}  
  
export default App;

四、避免使用匿名函数

避免使用匿名函数作为事件处理器或回调可以帮助实现性能优化

 原理:

主要基于JavaScript中的闭包和React的渲染机制。

  • 匿名函数会创建自己的闭包,这意味着每次组件渲染时,如果使用了匿名函数作为事件处理器,都会创建一个新的函数实例。这不仅会增加内存消耗,还可能导致不必要的重新渲染
  • 在类组件中,如果使用匿名函数作为回调,并且需要在该函数中访问this,则通常需要在构造函数中或使用箭头函数来绑定this。每次组件实例化时,这种绑定都会发生,这同样会增加开销。

 优化策略:

类组件中:

将事件处理器定义为类的方法

import React, { Component } from 'react';  
  
class MyComponent extends Component {  
  // 将事件处理器定义为类的方法  
  //handleClick方法被定义为一个类的方法,并且使用箭头函数来自动绑定this。这样,无论组件渲染多少次,handleClick的引用都不会改变,从而避免了不必要的重新渲染。
  handleClick = () => {  
    console.log('Button clicked!');  
  };  
  
  render() {  
    return (  
      <button onClick={this.handleClick}>Click Me</button>  
    );  
  }  
}  
  
export default MyComponent;

函数组件中:

1.使用箭头函数在组件外部定义回调

2.使用useCallback来缓存函数引用

import React, { useCallback } from 'react';  
  
const MyComponent = () => {  
  // 使用useCallback来缓存函数引用 
  const handleClick = useCallback(() => {  
    console.log('Button clicked!');  
  }, []); // 空依赖数组意味着这个函数不会在组件的生命周期内改变  
  
  return (  
    <button onClick={handleClick}>Click Me</button>  
  );  
};  
  
export default MyComponent;

五、避免使用内联对象

在React组件的JSX中使用内联对象时,每次组件渲染都会创建一个新的对象实例,这意味着每次渲染时该对象的引用都会改变。

 示例:

function MyComponent() {  
  // 定义内联样式对象  
  const styles = { margin: 0, padding: '10px', backgroundColor: 'lightblue' };  
  
  // 将styles传递给Component,而不是将内联对象直接写 style里
  return <Component style={styles}/>;  
}  

六、列表使用key属性

key属性用于帮助React识别列表中的哪些元素发生了变化、被添加或被删除。当你渲染一个元素列表时,为每个列表元素指定一个唯一的key可以极大地优化React的性能。

 原理:

React使用key来确定列表中元素的身份。当列表的props、state或上下文发生变化时,React会重新渲染列表。如果没有key,React会默认使用“就地更新”策略,这可能会导致性能问题和不期望的UI更新。

如果你为每个列表项提供了一个唯一的key,React能够识别出哪些元素是相同的(基于它们的key),哪些元素是不同的,从而只更新那些不同的元素。

示例:


const TodoList = ({ todos, onDelete }) => {  
  // 注意:这里的key属性是在TodoItem组件内部通过map函数设置的 
 const [todos, setTodos] = useState([  
    { id: 1, text: 'Learn React' },  
    { id: 2, text: 'Learn Redux' },  
    { id: 3, text: 'Build a project' },  
  ]);   
  return (  
    <ul>  
      {todos.map(todo => (  
        <TodoItem  
          key={todo.id} // 使用todo的唯一ID作为key  
          id={todo.id}  
          text={todo.text}  
          onDelete={onDelete}  
        />  
      ))}  
    </ul>  
  );  
};  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值