避免子组件re-rend:React性能优化的秘籍
2024-02-06 02:03:25
React的性能优化的一个方面就是避免组件的重复渲染(re-rend)。官方文档也提供了很过性能优化的奇技淫巧,且大部分都有一个比较明确的上下文。这里我们讨论两种常见的情况。
应该更新吗?
从字面意思上看,shouldComponentUpdate 这个生命周期函数明确告诉我们,他是用于优化,防止不必要的组件更新的 。那么在合适的情况,我们怎么正确使用它呢?
PureComponent 是 shouldComponentUpdate 的包装类,默认实现的就是shallow check。它会做以下3件事:
- 它不会自动绑定 this,但你可以手动绑定它
- 它会做shallow check 来判断组件的状态或者属性是否有变化
- 如果你想做更复杂的比较,你仍然可以重写它的 shouldComponentUpdate 方法
为什么是 shallow check 呢?
因为它会只检查你手动定义的 props 和 state 变化,如果你使用了 Redux,并使用 connect 对组件进行装饰,那么 由于连接组件的 props 是一层一层包起来的,所以进行比较的时候还是会执行重新渲染 。
因此,如果我们想使用 PureComponent 做深层比较的话,我们可以在 state 和 props 变化时,通过手动制造一个新的对象,将它们都 assign 到一个新对象上。这样,即使数据源是同一个,新对象和旧对象相比较,都是两个不同的对象了。
因此,在默认情况下,使用 PureComponent 相比于原生使用 shouldComponentUpdate 带来以下好处:
- 避免了手动绑定 this
- 强迫我们对所有的 props 和 state 手动实现比较
不要滥用 shouldComponentUpdate 和 PureComponent,因为它们不是万能的,存在如下不足:
- 它不能做层级深层比较
- 在类的构造函数中,调用 this.setState() 不会触发 shouldComponentUpdate
另外,当我们的需求明确的时候,我们可以手动编写 shouldComponentUpdate 的判断逻辑,实现比 PureComponent 更灵活的判断方法。
备忘录模式
备忘录模式和 shouldComponentUpdate/PureComponent 类似,它们都是为了减少组件重新渲染的次数。备忘录模式可以用来记住上次渲染时使用的 props 和 state,然后在组件更新时,比较新的 props 和 state 与上次渲染时保存的 props 和 state。如果它们没有发生变化,则组件不会重新渲染。
备忘录模式的实现方法有很多种,一种常见的方法是使用一个类来保存上次渲染时使用的 props 和 state。在组件更新时,比较新的 props 和 state 与上次渲染时保存的 props 和 state。如果它们没有发生变化,则组件不会重新渲染。
class MemoizedComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
memoizedProps: null,
memoizedState: null,
};
}
shouldComponentUpdate(nextProps, nextState) {
return (
this.state.memoizedProps !== nextProps ||
this.state.memoizedState !== nextState
);
}
componentDidUpdate(prevProps, prevState) {
this.setState({
memoizedProps: prevProps,
memoizedState: prevState,
});
}
render() {
return <WrappedComponent {...this.props} />;
}
}
备忘录模式的优点是,它可以很容易地应用于任何组件。缺点是,它可能会增加组件的复杂性,并且可能导致内存泄漏。
总之,shouldComponentUpdate/PureComponent 和备忘录模式都是非常有用的工具,可以用来减少组件重新渲染的次数。然而,在使用它们之前,应该仔细考虑组件的具体情况,以避免引入不必要的复杂性和内存泄漏。