React 性能优化(一): 减少不必要的组件更新
在 React 里会遇到这样一个场景: 父组件的状态发生变化会触发父组件的更新, 随后连同子组件一起更新.可是如果子组件没有和父组件产生依赖, 更新子组件其实是没有必要的.举个例子如下:
class Son extends React.Component {
render() {
console.log("触发儿子组件render");
return <p>我是儿子</p>;
}
}
export default class App extends React.Component {
constructor() {
super();
this.state = {
n: 1,
};
}
render() {
return (
<>
<p>我是爸爸</p>
<p>{this.state.n}</p>
<button
onClick={() => {
this.setState({ n: this.state.n + 1 });
}}
>
+1
</button>
<Son />
</>
);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
在 console 里可以看到, 每当+1
按钮被点击, Son
组件的 render 函数就会被执行一次.那我们应该通知 React 不要更新子组件.
shouldComponentUpdate
这里是这个钩子的详细定义和使用.
class Son extends React.Component {
shouldComponentUpdate() {
return false;
}
render() {
console.log("触发儿子组件render");
return <p>我是儿子</p>;
}
}
export default class App extends React.Component {
constructor() {
super();
this.state = {
n: 1,
};
}
render() {
return (
<>
<p>我是爸爸</p>
<p>{this.state.n}</p>
<button
onClick={() => {
this.setState({ n: this.state.n + 1 });
}}
>
+1
</button>
<Son />
</>
);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
PureComponent
如果不想每次手动调用上面的钩子, React 提供了PureComponent
来解决同样的问题:
class Son1 extends React.PureComponent {
render() {
console.log("触发儿子组件render");
return <p>我是儿子</p>;
}
}
export default class App1 extends React.Component {
constructor() {
super();
this.state = {
n: 1,
};
}
render() {
return (
<>
<p>我是爸爸</p>
<p>{this.state.n}</p>
<button
onClick={() => {
this.setState({ n: this.state.n + 1 });
}}
>
+1
</button>
<Son1 />
</>
);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
注意
值得一提的是,PureComponent
中 shouldComponentUpdate
对 props
做得只是浅层比较,不是深层比较,如果 props
是一个深层对象,就容易产生问题。
比如,两次渲染传入的某个 props
都是同一个对象,但是对象中某个属性的值不同,这在 PureComponent
眼里,props
没有变化,不会重新渲染,但是这>明显不是我们想要的结果。
函数组件使用
在函数组件里, React 提供了React.memo
来实现自定义更新:
const Son = React.memo((props) => {
console.log("触发儿子更新");
return <div>我是儿子{props.n}</div>;
});
export default class App1 extends React.Component {
constructor() {
super();
this.state = {
n: 1,
};
}
render() {
return (
<>
<p>我是爸爸</p>
<p>{this.state.n}</p>
<button
onClick={() => {
this.setState({ n: this.state.n + 1 });
}}
>
+1
</button>
<Son />
</>
);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29