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
可能更高效。