资讯专栏INFORMATION COLUMN

React PureComponent 源码解析

ZHAO_ / 466人阅读

摘要:自身的源码也很简单,节选如下上面的就是暴露给外部使用的。关于的源码分析就到这里。这是内部使用的一个工具集。不过也不是万能的,特定情况下自己实现可能更高效。

TL;DR

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

PureComponent 源码分析

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

import React, { PureComponent } from "react"

class Example extends PureComponent {
  render() {
    // ...
  }
}

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;

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

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

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

Advanced Performance

Reconciliation

React (Virtual) DOM Terminology

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

// 定义 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 。

shallowEqual, shallowCompare, PureRenderMixin 的联系

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

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

参考资料

PureComponent PR
shallowEqual
Shallow Compare
PureRenderMixin

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/80199.html

相关文章

  • Component,PureComponent源码解析

    摘要:首先是创建了一个构造函数,他的原型指到的原型然后创建了一个加上了和一样的属性这里为啥不用。的原型指向的实例修改原型的属性使其正确指向的构造函数,并挂一个的属性。 每次都信誓旦旦的给自己立下要好好学习react源码的flag,结果都是因为某个地方卡住了,或是其他原因没看多少就放弃了。这次又给自己立个flag-坚持看完react源码。为了敦促自己,特开设这样一个专栏来记录自己的学习历程,这...

    luqiuwen 评论0 收藏0
  • React源码解析React.Component()/PureComponent()

    摘要:只涉及了,其他均没有自己实现。这种组件的复用性是最强的。所以会新建,只继承的原型,不包括,以此来节省内存。 showImg(https://segmentfault.com/img/remote/1460000019783989); 一、React.Component() GitHub:https://github.com/AttackXiaoJinJin/reactExplain/...

    Cristalven 评论0 收藏0
  • 基于React版本16.4的源码解析(一)

    摘要:本次分析的源码采用的是的版本核心接口提供了处理的工具集我们先来看看做了什么事情即当为空时,返回不为时调用,最终返回一个数组这里说一下,可以通过传入的对所有子组件进行操作,具体使用方法看下图参数通过配合的例子把父组件的赋值给每个子组件我们先不 本次分析的源码采用的是16.4.1的版本 核心接口 showImg(https://segmentfault.com/img/bVbeT9f?w=...

    joywek 评论0 收藏0
  • 如何优化你的超大型React应用 【原创精读】

    摘要:往往纯的单页面应用一般不会太复杂,所以这里不引入和等等,在后面复杂的跨平台应用中我会将那些技术一拥而上。构建极度复杂,超大数据的应用。 showImg(https://segmentfault.com/img/bVbvphv?w=1328&h=768); React为了大型应用而生,Electron和React-native赋予了它构建移动端跨平台App和桌面应用的能力,Taro则赋...

    cfanr 评论0 收藏0
  • 如何优化你的超大型React应用 【原创精读】

    摘要:往往纯的单页面应用一般不会太复杂,所以这里不引入和等等,在后面复杂的跨平台应用中我会将那些技术一拥而上。构建极度复杂,超大数据的应用。 showImg(https://segmentfault.com/img/bVbvphv?w=1328&h=768); React为了大型应用而生,Electron和React-native赋予了它构建移动端跨平台App和桌面应用的能力,Taro则赋...

    codecook 评论0 收藏0

发表评论

0条评论

ZHAO_

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<