目录
一、使用shouldComponentUpdate和React.memo
1. 使用shouldComponentUpdate(类组件)
一、使用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.lazy
和Suspense
组件一起使用。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>
);
};
标签:函数,渲染,示例,React,props,组件,优化,加载
From: https://blog.csdn.net/hh_xi/article/details/142595119