React 性能优化:事件处理器的正确使用
React 性能优化:事件处理器的正确使用
组件的更新
React 组件有一个 componentShouldUpdate
生命周期方法可以让我们决定组件是否需要在本次渲染时更新。合理利用会带来性能上的提升。但如果你确定你的组件
props
和state
确定的情况下组件有确定的输出- 组件的
props
不存在复杂的嵌套对象,state
更新时是整个更新而不是修改其中的某个对象
那么直接使用 PureComponent
会省事很多。它与 Component
的区别在于,其实现了 shouldComponentUpdate
,对 props
和 state
进行了浅比较(shallowly compare)以判断组件是否需要更新。也就是这部分工作组件内部帮我们做了。
之所以要满足上面的条件,因为它做的是线比较,如果说传入的 props
里面是复杂对象,虽然对象里面属性值变更了,但对象引用没发生变化,PureComponent
检测不到输入的变化,也就不会重新渲染了。
事件处理器的绑定
图方便的情况下将事件处理器直接肉联在 JSX 中,考察下面的示例代码:
class Button extends React.PureComponent {
componentDidUpdate() {
console.count("Button updated");
}
render() {
return <button {...this.props}>click me</button>;
}
}
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 1
};
}
componentDidUpdate() {
console.count("MyComponent updated");
}
render() {
return (
<>
{this.state.count}
<Button
onClick={() => {
this.setState({ count: this.state.count + 1 });
}}
/>
</>
);
}
}
点击按钮增加数字,观察控制台的输出。
Button updated: 1
MyComponent updated: 1
Button updated: 2
MyComponent updated: 2
在点击按钮时,MyComponent
的状态会更新并且重新渲染,同时,根据输出信息可以清晰地知道 Button
组件也重新渲染了。但 Button
组件内部及外部的传入其他没什么变化。
同时 Button
组件看似使用了 PureComponent
来防止不必要的更新,但似乎并没有起到作用。
原因就在于这里通过肉联方式传递的 onClick
事件处理器。按钮点击后 MyComponent
重新渲染时,会生成新的事件处理器,然后传递到 Button
组件,虽然前后两次事件处理器一模一样,但其实已经不是同一个方法了,它们在内存中的引用是不同的。这导致 Button
组件的 shouldComponentUpdate
在进行浅比较时,检测到了变更,所以 Button
重新渲染了。
因此,我们要避免使用肉联的方式书写事件处理器。
解决方法可以是将它挂载到组件这个类上面,像下面这样:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 1
};
}
componentDidUpdate() {
console.count("MyComponent updated");
}
clickHandler = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<>
{this.state.count}
<Button onClick={this.clickHandler} />
</>
);
}
}
特别地,如果事件处理器中没有使用到 this
,那完全可以将其从组件中移出,定义在外部。
function clickHandler() {
console.log("hello");
}
class MyComponent extends React.Component {
render() {
return <Button onClick={clickHandler} />;
}
}
这样做的好处是所有组件都使用同一个函数,节省了开销。