1. 基本概念
1.1 什么是 PureComponent
PureComponent 是 React 提供的一个优化类组件,它通过自动实现 shouldComponentUpdate 生命周期方法,对 props 和 state 进行浅比较来决定是否需要重新渲染组件。
1.2 与 Component 的区别
jsx
// 普通 Component
class RegularComponent extends React.Component {
render() {
console.log('Regular Component render');
return <div>{this.props.value}</div>;
}
}
// PureComponent
class OptimizedComponent extends React.PureComponent {
render() {
console.log('Pure Component render');
return <div>{this.props.value}</div>;
}
}
2. 使用场景
React.PureComponent 是 React 中一个非常有用的类,它是 React.Component 的一个变种,主要用于优化性能。PureComponent 会自动实现 shouldComponentUpdate 方法,并基于 浅比较(shallow comparison) 来决定组件是否需要重新渲染。
PureComponent 的使用场景
组件的 props 和 state 变化不复杂且不深层嵌套
如果组件的 props 或 state 是简单类型(如 string、number、boolean 等),或者是浅层嵌套的对象数组,PureComponent 的性能优化非常有效。
PureComponent 会对 props 和 state 进行浅比较,如果值没有变化,组件就不会重新渲染。
使用场景:
比如当你有一个只渲染传入文本的组件,如果文本没有变化,PureComponent 会避免不必要的重新渲染。
javascript
class MyComponent extends React.PureComponent {
render() {
return <div>{this.props.text}</div>;
}
}
组件渲染的内容相对静态,且频繁更新的场合
当某个组件被频繁地父组件更新(比如父组件经常调用 setState),但该组件的部分 props 或 state 没有变化时,使用 PureComponent 可以有效避免不必要的渲染,从而提升性能。
使用场景:
比如你有一个列表组件,父组件可能会触发更新,但列表项本身不会改变,使用 PureComponent 可以减少渲染的次数。
javascript
class ListItem extends React.PureComponent {
render() {
return <li>{this.props.name}</li>;
}
}
需要避免深度比较的情况下
PureComponent 使用 浅比较 来判断 props 和 state 是否变化。如果你的 props 和 state 中包含的是对象或数组,并且这些对象是由父组件传递过来的新引用(即使它们的内容没变),PureComponent 也会认为它们发生了变化,导致重新渲染。
使用 PureComponent 可以避免显式编写 shouldComponentUpdate 来实现自定义的浅比较,特别是在简单应用中可以减少代码量。
避免在 shouldComponentUpdate 中手动比较
PureComponent 会自动处理浅比较,而你不需要手动编写 shouldComponentUpdate。这在很多情况下可以减少代码复杂度,避免开发者手动处理复杂的比较逻辑。
大白话:子组件不依赖当前状态变化时子组件使用PureComponent,父组件改变的值不要引起render渲染的时候,避免引用地址不变的对象属性值修改
2.1 简单数据类型的 props
jsx
// 适合使用 PureComponent 的场景
class NumberDisplay extends React.PureComponent {
render() {
return <div>{this.props.number}</div>;
}
}
// 父组件
class Parent extends React.Component {
state = { number: 1 };
updateNumber = () => {
this.setState({ number: 2 });
};
render() {
return (
<div>
<NumberDisplay number={this.state.number} />
<button onClick={this.updateNumber}>Update</button>
</div>
);
}
}
2.2 复杂数据结构的处理
jsx
// 不恰当的使用方式
class BadExample extends React.PureComponent {
render() {
// 每次渲染都创建新对象,导致浅比较失效
const data = { value: this.props.value };
return <div>{data.value}</div>;
}
}
// 正确的使用方式
class GoodExample extends React.PureComponent {
render() {
return <div>{this.props.data.value}</div>;
}
}
// 父组件
class Parent extends React.Component {
state = {
data: { value: 'hello' }
};
updateValue = () => {
// 创建新的对象引用
this.setState({
data: { ...this.state.data, value: 'world' }
});
};
}
3. 性能优化实践
3.1 避免内联对象
jsx
// 错误示例
class Parent extends React.Component {
render() {
return (
<PureChild
style={{ color: 'red' }} // 每次渲染都创建新对象
data={{ value: 42 }} // 每次渲染都创建新对象
/>
);
}
}
// 正确示例
class Parent extends React.Component {
style = { color: 'red' }; // 类属性,保持引用不变
data = { value: 42 }; // 类属性,保持引用不变
render() {
return (
<PureChild
style={this.style}
data={this.data}
/>
);
}
}
3.2 避免内联函数
jsx
// 错误示例
class Parent extends React.Component {
render() {
return (
<PureChild
onClick={() => console.log('clicked')} // 每次渲染都创建新函数
/>
);
}
}
// 正确示例
class Parent extends React.Component {
handleClick = () => {
console.log('clicked');
};
render() {
return (
<PureChild
onClick={this.handleClick} // 使用类方法,保持引用不变
/>
);
}
}
3.3 使用不可变数据
jsx
class TodoList extends React.PureComponent {
state = {
todos: []
};
// 正确的更新方式
addTodo = (todo) => {
this.setState(prevState => ({
todos: [...prevState.todos, todo] // 创建新数组
}));
};
updateTodo = (index, newTodo) => {
this.setState(prevState => ({
todos: prevState.todos.map((todo, i) =>
i === index ? { ...todo, ...newTodo } : todo
)
}));
};
// 错误的更新方式
wrongUpdate = (index, newTodo) => {
const { todos } = this.state;
todos[index] = newTodo; // 直接修改数组
this.setState({ todos }); // PureComponent 无法检测到变化
};
}
4. 常见陷阱和解决方案
4.1 嵌套对象的比较
jsx
class NestedDataComponent extends React.PureComponent {
state = {
user: {
profile: {
name: 'John',
age: 25
}
}
};
// 错误的更新方式
wrongUpdate = () => {
const { user } = this.state;
user.profile.age = 26; // 直接修改嵌套对象
this.setState({ user });
};
// 正确的更新方式
correctUpdate = () => {
this.setState(prevState => ({
user: {
...prevState.user,
profile: {
...prevState.user.profile,
age: 26
}
}
}));
};
}
4.2 数组更新
jsx
class ArrayComponent extends React.PureComponent {
state = {
items: [1, 2, 3]
};
// 错误的数组操作
wrongArrayUpdate = () => {
const { items } = this.state;
items.push(4); // 直接修改数组
this.setState({ items });
};
// 正确的数组操作
correctArrayUpdate = () => {
this.setState(prevState => ({
items: [...prevState.items, 4]
}));
};
// 正确的过滤操作
filterItems = (predicate) => {
this.setState(prevState => ({
items: prevState.items.filter(predicate)
}));
};
}
5. 最佳实践
5.1 何时使用 PureComponent
- 组件的 props 和 state 是简单数据类型
- 组件的渲染输出完全由 props 和 state 决定
- 组件需要频繁重渲染
- 组件的 props 和 state 变化频率较低
5.2 何时避免使用 PureComponent
- 组件的 props 经常变化
- 组件的渲染逻辑复杂
- 组件需要处理深层数据结构
- 组件依赖于外部状态或上下文
5.3 性能优化建议
jsx
// 1. 使用 memoization
class OptimizedComponent extends React.PureComponent {
memoizedValue = memoize(value => expensiveOperation(value));
render() {
const processedValue = this.memoizedValue(this.props.value);
return <div>{processedValue}</div>;
}
}
// 2. 合理拆分组件
class ParentComponent extends React.Component {
render() {
return (
<div>
<PureChild1 data={this.props.data1} />
<PureChild2 data={this.props.data2} />
</div>
);
}
}
6. 总结
6.1 核心要点
- PureComponent 通过浅比较优化性能
- 适用于简单数据结构的场景
- 需要注意引用类型的处理
- 配合不可变数据使用效果最佳
6.2 使用建议
- 合理评估使用场景
- 避免内联对象和函数
- 正确处理复杂数据结构
- 注意性能优化的边界条件