React PureComponent 源码解析

前端之家收集整理的这篇文章主要介绍了React PureComponent 源码解析前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

TL;DR

@H_404_2@React 15.3.0 新增了一个 PureComponent 类,以 ES2015 class 的方式方便地定义纯组件 (pure component)。这篇文章分析了一下源码实现,并衍生探讨了下 shallowComparePureRenderMixin。相关的 GitHub PR 在 这里

PureComponent 源码分析

@H_404_2@这个类的用法很简单,如果你有些组件是纯组件,那么把继承类从 Component 换成 PureComponent 即可。当组件更新时,如果组件的 propsstate 都没发生改变,render 方法就不会触发,省去 Virtual DOM 的生成和比对过程,达到提升性能的目的。

import React,{ PureComponent } from 'react'

class Example extends PureComponent {
  render() {
    // ...
  }
}
@H_404_2@PureComponent 自身的源码也很简单,节选如下:

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;
@H_404_2@上面的 ReactPureComponent 就是暴露给外部使用的 PureComponent 。可以看到它只是继承了 ReactComponent 再设定了一下 isPureReactComponent 属性ComponentDummy 是典型的 JavaScript 原型模拟继承的做法,对此有疑惑的可以看 我的另一篇文章 。另外,为了避免原型链拉长导致方法查找的性能开销,还用 Object.assign方法ReactComponent 拷贝过来了。

@H_404_2@跟 PureRenderMixin 不一样的是,这里完全没有实现 shouldComponentUpdate。那 PureComponent 的 props/state 比对是在哪里做的呢?答案是 ReactCompositeComponent

@H_404_2@ReactCompositeComponent 这个类的信息太少,我只能推测它是负责实际渲染并维护组件实例的对象。建议大家从高层次了解 React 对组件的更新机制即可。以下几篇官方文档看完就足够了。

@H_404_2@这个类的代码改动很多,但关键就在 这里 。下面是我简化后的代码片段:

// 定义 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);
  }
}
@H_404_2@简而言之,ReactCompositeComponent 会在 mount 的时候判断各个组件的类型,设定 _compositeType ,然后根据这个类型来判断是非需要更新组件。这个 PR 中大部分改动都是 因为加了 CompositeTypes 而做的调整性工作,实际跟 PureComponent 有关的就是 shallowEqual 的那两行。

@H_404_2@关于 PureComponent 的源码分析就到这里。其他的就都是细节和测试,有兴趣的可以自己看看 PR 。

shallowEqual,shallowCompare,PureRenderMixin 的联系

@H_404_2@我们知道在 PureComponent 出现之前,shallowComparePureRenderMixin 都可以做一样的事情。于是好奇看了一下后两者的代码

@H_404_2@shallowCompare 的源码:

var shallowEqual = require('shallowEqual');

function shallowCompare(instance,nextProps,nextState) {
  return (
    !shallowEqual(instance.props,nextProps) ||
    !shallowEqual(instance.state,nextState)
  );
}

module.exports = shallowCompare;
@H_404_2@PureRenderMixin 的源码:

var shallowCompare = require('shallowCompare');

var ReactComponentWithPureRenderMixin = {
  shouldComponentUpdate: function(nextProps,nextState) {
    return shallowCompare(this,nextState);
  },};

module.exports = ReactComponentWithPureRenderMixin;
@H_404_2@可以看到,shallowCompare 依赖 shallowEqual ,做的是跟刚才在 ReactCompositeComponent 里一样的事情 -- 对比 props 和 state 。这个工具函数一般配合组件的 shouldComponentUpdate 一起使用,而这就是 PureRenderMixin 做的事情。不过 PureRenderMixin 是配合 React.createClass 这种老的组件定义方式使用的,在 ES2015 class 里用起来不是很方便,这也是 PureComponent 诞生的原因之一。

@H_404_2@最后 shallowEqual 这玩意定义在哪里呢?它其实不是 React 的一部分,而是 fbjs 的一部分。这是 Facebook 内部使用的一个工具集。

小结

@H_404_2@React 之前一直没有针对 ES2015 class 的纯组件写法,虽然自己实现起来并不麻烦,但这也算给出了一个官方的解决方案,可以不再依赖 addon 了。不过 PureComponent 也不是万能的,特定情况下自己实现 shouldComponentUpdate 可能更高效。

参考资料

@H_404_2@PureComponent PR
shallowEqual
Shallow Compare
PureRenderMixin

猜你在找的React相关文章