react版本16.10.1(截止到2019年9月28日)
那么在这篇文章里我会介绍一下关于React15.3的一个新特性PureComponent.

开篇简单讲解一下:

React15.3中新加了一个PureComponent类,取代之前的PureRenderMixin , PureComponent可以进行React性能优化,减少不必要的render渲染次数,使用时只要把继承类从Component换成PureComponent。

PureComponent的原理是继承了Component类,自动加载shouldComponentUpdate函数,当组件更新时,shouldComponentUpdate对props和state进行了一层浅比较,如果组件的props和state都没有发生改变,render方法就不会触发,省去Virtual DOM的生成和对比过程,达到提升性能的目的。

那么在这里我们从头开始,先来讲解一下shouldComponentUpdate这个生命周期

在PureComponent之前,我们经常看到优化react性能最常见的手段之一就是在react的生命周期函数shouldComponentUpdate里判断props或state的数据是否发生变化,通过返回ture(更新)和false(不更新)来阻止不必要的render.
首先我们来看两端代码:
App.js文件

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
import React, { Component } from 'react'
import ShouldComponentUpdateList from './ShouldComponentUpdateList'

// 容器组件
export default class App extends Component {
state = {
data: []
}
componentDidMount() {
// 定时任务,每隔一秒更新数据
setInterval(() => {
this.setState({
data: [
{ title: 'react line 1' },
{ title: 'react line 2' },
]
})
}, 1000)
}
render() {
return(
<div>
{
this.state.data.map((item, index) => (
<ShouldComponentUpdateList key={index} list={item} />
))
}
</div>
)
}
}

ShouldComponentUpdateList组件内容为:

1
2
3
4
5
6
7
8
export default class List extends Component {
render() {
console.log('list render')
return(
<div>{this.props.list.title}</div>
)
}
}

命令行运行npm start,浏览器查看输出发现每一秒都会执行一次render方法,明明数据没有发生变化,但是react还是发生渲染,造成了不必要的渲染浪费。

用shouldComponentUpdate改进的方法

只需要在shouldComponentUpdate里加上判断,再次查看输出结果,定时任务的数据没有发生改变,不会再渲染render函数
ShouldComponentUpdateList组件内容更改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
export default class List extends Component {
// 在shouldComponentUpdate里判断props传递的数据没有发生变化,则不需要render
shouldComponentUpdate(nextProps) {
// 返回值为true则render,为false则不render.
if(nextProps.list.title === this.props.list.title) {
return false
}
return true
}
render() {
console.log('list render')
return(
<div>{this.props.list.title}</div>
)
}
}

PureComponent使用

除了使用shouldComponentUpdate来判断是否需要更新组件,还可以用PureComponent, PureComponent实际上自动加载shouldComponentUpdate函数,当组件更新时,shouldComponentUpdate对props和state进行了一层浅比较.
新建PureComponentList组件,用PureComponent代替Component:

1
2
3
4
5
6
7
8
9
import React, { PureComponent } from 'react'
export default class List extends PureComponent {
render() {
console.log('list render')
return(
<div>{this.props.list.title}</div>
)
}
}

App.js组件传入

1
2
3
this.state.data.map((item, index) => (
<PureComponentList key={index} list={item}/>
))

然后查看浏览器输出结果,惊奇地发生,PureComponent并没有阻止不必要render,这是为什么呢?因为前面我们说到PureComponent的shouldComponentUpdate只对props和state进行浅比较,也就是this.props = { list: { title: ‘react line1’ } },nextProps = { list: { title: ‘react line1’ } },作浅比较的话this.props当然不等于next.props.

所以重点来了:如何才能真正的用好PureComponent这个组件呢

1)从源码角度来分析:对于对象的比较仅仅通过if (objA === objB) { return true; }来判断
而let a = { list: { title: ‘react line1’ } },let b = { list: { title: ‘react line1’ } }, a === b值为false,那么会返回return true。那么会继续的渲染更新。所以这就很好的解释了上面PureComponent并没有阻止不必要render的原因。
2)由上面探究PureComponent源码我们知道,PureComponent的组件在props或者state的属性值是对象的情况下,并不能阻止不必要的渲染,是因为自动加载的shouldComponentUpdate里面做的只是浅比较,所以想要用PureComponent的特性,应该遵守原则:
(重点)确保数据类型是值类型
(重点)如果是引用类型,不应当有深层次的数据变化(解构)。

那么下面我将会对代码进行一次更改

App.js文件

1
2
3
4
this.state.data.map((item, index) => (
// <PureComponentList key={index} list={item}/>
<PureComponentList key={index} title={item.title}/>
))

这样的话就达到我们想要的效果咯~

拓展:React.memo

在使用PureComponent的时候,只能把react组件写成是class的形式,不能使用函数的形式;react v16.6.0之后,可以使用React.memo来实现函数式的组件,也有了PureComponent的功能。

List组件的PureComponent:

1
2
3
const ListComponent = React.momo(() => (
<div>{this.props.data || 'loading'}</div>
))

参考资料:React性能优化:PureComponent的使用原则