React性能优化: 剑走偏锋(二)

简介

上一次, 咱们介绍了使用forceUpdate来重写shouldComponent生命周期(React性能优化: 剑走偏锋(一)). 这一次咱们继续这个话题.javascript

react提供了一个PureComponent, 也是重写了shouldComponentUpdate, 不过它作的是一个浅比较. 也就是说它能知足的场景很是有限. 若是项目中使用了immutable数据结构, 那么改比较基本就是失效了.java

此次咱们也定义个组件XPureComponent组件, 重写shouldComponentUpdate生命周期, 让它可以知足更多的场景. 不限制state和props的数据结构. 能够是任意的. 好比简单对象, 复杂对象, immutable对象, HTMLELEMENT节点等任意对象.node

XPureComponent

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>
    }
}
复制代码

compare.js的实现

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;

复制代码

经测试, 比较所花的时间是毫秒级别, 组件避免了大量的重复渲染. 性能提高很是明显. 目前该方法已经运用到实际的项目中.

相关文章
相关标签/搜索