上一次, 咱們介紹了使用forceUpdate來重寫shouldComponent生命週期(React性能優化: 劍走偏鋒(一)). 這一次咱們繼續這個話題.javascript
react提供了一個PureComponent, 也是重寫了shouldComponentUpdate, 不過它作的是一個淺比較. 也就是說它能知足的場景很是有限. 若是項目中使用了immutable數據結構, 那麼改比較基本就是失效了.java
此次咱們也定義個組件XPureComponent組件, 重寫shouldComponentUpdate生命週期, 讓它可以知足更多的場景. 不限制state和props的數據結構. 能夠是任意的. 好比簡單對象, 複雜對象, immutable對象, HTMLELEMENT節點等任意對象.node
import React from 'react';
import equals from './compare';
/** * @template Props * @template State * @template Snapshot * * @extends {React.Component<Props, State, Snapshot>} */
class XPureComponent extends React.Component {
/** * @override */
shouldComponentUpdate(nextProps, nextState) {
// 按照先State再Props的順序比較,不一致則須要更新。
return !(equals(this.state, nextState) && equals(this.props, nextProps));
}
}
export default XPureComponent;
複製代碼
後面全部class組件就繼承這個XPureComponentreact
class XApp extends XPureComponent {
render(){
return <div></div>
}
}
複製代碼
import { isImmutable, is } from 'immutable';
import { isEqual } from 'lodash';
const excludeKeys = ['__proto__', 'children'];
/** * 判斷是否爲dom節點對象 * @param node * @returns {boolean} */
const isDomNode = node => {
return node && node.nodeName;
};
/** * 比較兩個大對象. * @param {Any} first * @param {Any} second */
const equals = (first, second) => {
//判斷是否爲dom節點對象.
if(isDomNode(first) && isDomNode(second)){
return first.isSameNode(second);
}
// 先判斷是否爲同一個對象.
if(first === second){
return true;
}
// 兩個都是
if (isImmutable(first) && isImmutable(second)) {
return is(first, second);
}
// 只有一個是
if (isImmutable(first) || isImmutable(second)) {
return false;
}
// 兩個都是普通對象.
// 比較第一層.
if (
first &&
second &&
typeof first === 'object' &&
typeof second === 'object'
) {
const firstKeys = Object.keys(first);
const secondKeys = Object.keys(second);
// 先比較長度
if (firstKeys.length !== secondKeys.length) {
return false;
}
// 再比較key.
const equal = isEqual(firstKeys, secondKeys);
if (!equal) {
return false;
}
// key相同的狀況下.
for (let i = 0; i < firstKeys.length; i++) {
const key = firstKeys[i];
const isExcluded = excludeKeys.findIndex(k => k === key) !== -1;
// 遞歸比較.
if (!isExcluded && !equals(first[key], second[key])) {
return false;
}
}
return true;
}
return isEqual(first, second);
};
export default equals;
複製代碼