React 15.3.0 新增了一个 PureComponent
类,以 ES2015 class 的方式方便地定义纯组件 (pure component)。这篇文章分析了一下源码实现,并衍生探讨了下 shallowCompare
和 PureRenderMixin
。相关的 GitHub PR 在 这里 。html
这个类的用法很简单,若是你有些组件是纯组件,那么把继承类从 Component
换成 PureComponent
便可。当组件更新时,若是组件的 props
和 state
都没发生改变,render 方法就不会触发,省去 Virtual DOM 的生成和比对过程,达到提高性能的目的。react
import React, { PureComponent } from 'react' class Example extends PureComponent { render() { // ... } }
PureComponent
自身的源码也很简单,节选以下:git
function ReactPureComponent(props, context, updater) { // Duplicated from ReactComponent. this.props = props; this.context = context; this.refs = emptyObject; // We initialize the default updater but the real one gets injected by the // renderer. this.updater = updater || ReactNoopUpdateQueue; } function ComponentDummy() {} ComponentDummy.prototype = ReactComponent.prototype; ReactPureComponent.prototype = new ComponentDummy(); ReactPureComponent.prototype.constructor = ReactPureComponent; // Avoid an extra prototype jump for these methods. Object.assign(ReactPureComponent.prototype, ReactComponent.prototype); ReactPureComponent.prototype.isPureReactComponent = true;
上面的 ReactPureComponent
就是暴露给外部使用的 PureComponent
。能够看到它只是继承了 ReactComponent
再设定了一下 isPureReactComponent
属性。ComponentDummy
是典型的 JavaScript 原型模拟继承的作法,对此有疑惑的能够看 个人另外一篇文章 。另外,为了不原型链拉长致使方法查找的性能开销,还用 Object.assign
把方法从 ReactComponent
拷贝过来了。github
跟 PureRenderMixin
不同的是,这里彻底没有实现 shouldComponentUpdate
。那 PureComponent
的 props/state 比对是在哪里作的呢?答案是 ReactCompositeComponent
。segmentfault
ReactCompositeComponent
这个类的信息太少,我只能推测它是负责实际渲染并维护组件实例的对象。建议你们从高层次了解 React 对组件的更新机制便可。如下几篇官方文档看完就足够了。数组
这个类的代码改动不少,但关键就在 这里 。下面是我简化后的代码片断:oop
// 定义 CompositeTypes var CompositeTypes = { ImpureClass: 0, // 继承自 Component 的组件 PureClass: 1, // 继承自 PureComponent 的组件 StatelessFunctional: 2, // 函数组件 }; // 省略一堆代码,由于加入了 CompositeTypes 形成的调整 // 这个变量用来控制组件是否须要更新 var shouldUpdate = true; // inst 是组件实例 if (inst.shouldComponentUpdate) { shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState, nextContext); } else { if (this._compositeType === CompositeType.PureClass) { // 用 shallowEqual 对比 props 和 state 的改动 // 若是都没改变就不用更新 shouldUpdate = !shallowEqual(prevProps, nextProps) || !shallowEqual(inst.state, nextState); } }
简而言之,ReactCompositeComponent
会在 mount 的时候判断各个组件的类型,设定 _compositeType
,而后根据这个类型来判断是非须要更新组件。这个 PR 中大部分改动都是 由于加了 CompositeTypes
而作的调整性工做,实际跟 PureComponent
有关的就是 shallowEqual
的那两行。
关于 PureComponent
的源码分析就到这里。其余的就都是细节和测试,有兴趣的能够本身看看 PR 。
咱们知道在 PureComponent
出现以前,shallowCompare
和 PureRenderMixin
均可以作同样的事情。因而好奇看了一下后二者的代码。
shallowCompare
的源码:
var shallowEqual = require('shallowEqual'); function shallowCompare(instance, nextProps, nextState) { return ( !shallowEqual(instance.props, nextProps) || !shallowEqual(instance.state, nextState) ); } module.exports = shallowCompare;
PureRenderMixin
的源码:
var shallowCompare = require('shallowCompare'); var ReactComponentWithPureRenderMixin = { shouldComponentUpdate: function(nextProps, nextState) { return shallowCompare(this, nextProps, nextState); }, }; module.exports = ReactComponentWithPureRenderMixin;
能够看到,shallowCompare
依赖 shallowEqual
,作的是跟刚才在 ReactCompositeComponent
里同样的事情 -- 对比 props 和 state 。这个工具函数通常配合组件的 shouldComponentUpdate
一块儿使用,而这就是 PureRenderMixin
作的事情。不过 PureRenderMixin
是配合 React.createClass
这种老的组件定义方式使用的,在 ES2015 class 里用起来不是很方便,这也是 PureComponent
诞生的缘由之一。
最后 shallowEqual
这玩意定义在哪里呢?它其实不是 React 的一部分,而是 fbjs 的一部分。这是 Facebook 内部使用的一个工具集。
React 以前一直没有针对 ES2015 class 的纯组件写法,虽然本身实现起来并不麻烦,但这也算给出了一个官方的解决方案,能够再也不依赖 addon 了。不过 PureComponent
也不是万能的,特定状况下本身实现 shouldComponentUpdate
可能更高效。