从React源码来理解PureComponent的正确打开方式
前言
性能优化一直是前端热于讨论的话题,我们可以根据不同的场景、框架来做不同的措施,而React框架的特点是“可玩性高”,他只提供简单的响应式封装和JSX语法糖,而给我们大量的优化空间,今天我们介绍一个非常强大的属性:PureComponent
DEMO
在开始之间,我们看一段代码,他或许能给我们一些思考:
1 | import React from 'react'; |
当我们点击Click的时候。App组件state中的num发生变化,然后触发了App的render,有因为Item组件为App的子组件,所以Item的render也需要触发:

我们思考,子组件Item中的props并没有发生变化,也没必要去render一次,更没必要去触发Item组件的对比virtual DOM,这个时候,我们或许想起了是否能利用shouldComponentUpdate这个生命周期来去组织Item组件的render触发,其实PureComponent就是利用这个生命周期函数提高组件的性能。
PureComponent
PureComponent是React15.3中新加的一个类,顾名思义, pure 是纯的意思,PureComponent 也就是纯组件,取代其前身 PureRenderMixin , PureComponent 是优化 React 应用重要的方法之一,易于实施,只要把继承类从 Component 换成 PureComponent 即可,可以减少不必要的 render 操作的次数,从而提高性能。
原理
我们先来理解React响应式的原理:
- 组件state或props改变
- 通过shouldComponentUpdate来判断是否需要update
- 如果需要,则进行render()
- 根据具state、props得到virtual DOM,与之前的virtual DOM对比
- virtual DOM改变,则渲染新的DOM
我们看到,从第二步开始已经在进行密集的计算了,假如要提升性能,这可以从两个角度去优化:
- 不要触发 render function
- 保持 virtual DOM 的一致
最理想的情况是组织触发没必要的render function,比如最上面的Item组件,而PureComponent的原理就是当组件更新时,如果组件的 props 和 state 都没发生改变, render 方法就不会触发,省去 Virtual DOM 的生成和比对过程,达到提升性能的目的。具体就是 React 自动帮我们做了一层浅比较,我们可以查看这部分的源码:
1 | if (this._compositeType === CompositeTypes.PureClass) { |
而 shallowEqual 会比较 Object.keys(state | props) 的长度是否一致,每一个 key 是否两者都有,并且是否是一个引用,也就是只比较了第一层的值,确实很浅,所以深层的嵌套数据是对比不出来的,因此,我们使用PureComponent的时候尤其要注意场景:组件state、props不容易发生变化。
实践
我们继续改造上面的DEMO例子:
1 | import React from 'react'; |
当我们改变父组件的state后,子组件Item因为添加了PureComponent属性,则会自动去判断props、state前后是否一致,一致则不触发render,所以这次我们看到:

避免不必要的render对组件能带来较大的性能提升,但是PureComponent并不是适应任何场景的。
PureComponent陷阱
为什么说PureComponent有陷阱,那就是因为PureComponent的默认的shouldComponentUpdate只是做了一层浅对比,我们看一个例子:
1 | import React from 'react' |
这里,我们点击Click,改变state,但是view并没有发生改变,这是为什么呢?
因为shallowEqual只是一层浅比较,当它判断他们指向的内存是一致的,那么就shouldComponentUpdate就直接返回false,render就不渲染了,因此,我们非常不建议在组件的props或者state易变的情况下使用PureComponent。
那么我们如何解决呢?
1 | handleClick = () => { |
只需要创建一个新的数组,再赋值给list,这样shallowEqual检测到他们并不是指向同一块内存,则会触发render。
在PureComponent改动一个state需要这样才能生效,那我们思考,在这种情况下,是否真的需要使用PureComponent?他会给我们带来性能提升吗?
答案是否定的:
我们来看一下,当组件的state、props易变的情况下,使用PureComponent会经历一个怎样的过程:
- state发生改变
- shouldComponentUpdate函数通过shallowEqual来判断prev与next是否一致
- 判断不一致,触发render
在state、props易变的组件,我们使用PureComponent,每次改变都需要经过一层shouldComponentUpdate里面的shallowEqual,这显然是对性能不友好的,所以这是一个PureComponent陷阱,我们一定要根据组件的条件来选择是否使用PureComponent,如果胡乱使用PureComponent,不仅代码可能会出bug,还会造成“性能反优化”的尴尬场景。
最后
当我们使用一个新的特性的时候,一定要了解其使用的场景,冒着侥幸的心理去做一件事肯定会适得其反,PureComponent只是适合用在简单、状态不易变的组件当中。
To save time is to lengthen life. :)