react的Component和PureComponent的区别

结论

PureComponent是在Component的基础上对shouldComponentUpdate这部分进行了优化,通过对新旧state和新旧props各进行一次浅比较,通过判断是否发生变化来确认是否需要进行更新,以此来避免大量的重复渲染,优化性能。我们通过一段代码来看下两者的行为

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

class ChildComponent extends React.Component {
render() {
console.log('ChildComponent render')
return(
<div>
{this.props.numbers}
</div>
)
}
}

class ChildComponent extends React.PureComponent {
render() {
console.log('ChildComponent render')
return(
<div>
{this.props.numbers}
</div>
)
}
}
class MainComponent extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
this.state = {
numbers: [0]
}
}
handleClick() {
const arr = this.state.numbers;
arr.push(1);
this.setState({
numbers: arr
})
}
render() {
console.log('MainComponent render')
return(
<div>
<button onClick={this.handleClick} >click me</button>
<ChildComponent numbers={this.state.numbers}/>
</div>
)

}
}

export default MainComponent

在点击按钮时,MainComponent每次都会执行render函数,但ChildComponent只有继承PureComponent时,才会执行render,在react的源码中,PureComponent在判断是否需要更新组件时,进行了以下的比较:

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
36
37
38
39
40
41
42
43
44
45
46
47
shouldUpdate = !shallowEqual(oldProps, props) || !shallowEqual(oldState, state);


//shallowEqual函数实现

function is(x: any, y: any) {
return (
//这边是为了处理 0 === -0 和 NaN !== NaN的情况
(x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y)
);
}

const hasOwnProperty = Object.prototype.hasOwnProperty;

function shallowEqual(objA: mixed, objB: mixed): boolean {
if (is(objA, objB)) {
return true;
}

if (
typeof objA !== 'object' ||
objA === null ||
typeof objB !== 'object' ||
objB === null
) {
return false;
}

const keysA = Object.keys(objA);
const keysB = Object.keys(objB);

if (keysA.length !== keysB.length) {
return false;
}

// Test for A's keys different from B.
for (let i = 0; i < keysA.length; i++) {
if (
!hasOwnProperty.call(objB, keysA[i]) ||
!is(objA[keysA[i]], objB[keysA[i]])
) {
return false;
}
}

return true;
}

注意:这个代码示例中, handleClick 方法中给state对象push元素时,用的是引用的方式,所以,当使用PureComponent时,因为我们操作的时同一个数据对象,所以就算修改了state,组件也不会进行更新,但是因为Component不进行这个处理,所以页面的数据可以正常发生变化,这也是使用PureComponent必须留意的地方。我们必须通过浅拷贝或者immutable的一些库,来避免这种错误的发生。
用浅拷贝的修改方式如下

1
2
3
4
5
6
7
8
 handleClick() {
const arr = this.state.numbers;
let newArr = [...arr]
newArr.push(1)
this.setState({
numbers: newArr
})
}

用immutableJS的修改方式如下

1
2
3
4
5
6
7
8
9
10
11
12
13
//使用immutablejs的List来初始化state
this.state = {
numbers: List([0])
}

//使用List的push函数重新生成arr
handleClick() {
const arr = this.state.numbers;
let newArr = arr.push(1)
this.setState({
numbers: newArr
})
}