阅读 78

React@15.6.2源码解析---从 ReactDOM.render 到页面渲染(3)ReactUpdates

上篇博客介绍了instantiateReactComponent方法,方法内部实现还是很简单的,那么回到react得渲染流程里,接下来就是执行ReactUpdates.batchedUpdates方法,所以本篇博客讲解ReactUpdates操作,从名字可以看出这是处理React更新的文件,内部用到一个很重要得东西就是Transaction,对于这个词想必大家还是很熟悉得。ReactUpdates内部实现也是很简单,内部封装了batchedUpdatesenqueueUpdateflushBatchedUpdates等方法,而这三个是最重要的操作。内部也用到了一些全局依赖注入得东西,和上一篇博客提到的是一样的。

ReactUpdates

var ReactUpdates = {
  /**
   * React references `ReactReconcileTransaction` using this property in order
   * to allow dependency injection.
   *
   * @internal
   */
  ReactReconcileTransaction: null,

  batchedUpdates: batchedUpdates,
  enqueueUpdate: enqueueUpdate,
  flushBatchedUpdates: flushBatchedUpdates,
  injection: ReactUpdatesInjection,
  asap: asap
};
复制代码

这是最终的ReactUpdates对象,内部得属性还是很少的,这边的asap不做说明,我没看懂这个东西,还望各位大牛指导!,下面就一一为这些属性说明

ReactUpdates.injection

injection放在第一个是因为,这个方法在全局以来注入的时候就被执行了,论执行顺序的话,这个方法是第一个被执行的方法,而且下面的方法需要用到依赖注入的东西,本着不让看官不知道这个变量是从哪里来的原则,所以这个方法放在第一位。

injection对应着ReactUpdatesInjection

// ReactUpdates.js
var ReactUpdatesInjection = {
  injectReconcileTransaction: function (ReconcileTransaction) {
    !ReconcileTransaction ? /**/
    ReactUpdates.ReactReconcileTransaction = ReconcileTransaction;
  },

  injectBatchingStrategy: function (_batchingStrategy) {
    !_batchingStrategy ? /**/
    batchingStrategy = _batchingStrategy;
  }
};
复制代码

内部有两个函数,分别用于给ReactUpdates.ReactReconcileTransaction 和 外部闭包的batchingStrategy变量赋值。每个函数内部都会首先对传入的参数做一个是否存在的验证。那么来看传递的实参是什么,回到ReactDefaultInjection.js文件

// ReactDefaultInjection.js
ReactInjection.Updates.injectReconcileTransaction(ReactReconcileTransaction);
ReactInjection.Updates.injectBatchingStrategy(ReactDefaultBatchingStrategy);
复制代码

那么这边可以看到一个是ReactReconcileTransaction 对象,一个是ReactDefaultBatchingStrategy对象,这两个都是重点。拿起小笔开始画重点了!!!依旧留坑,以后会说的。本人是讲信用的人,你看这两篇博客不就是在填坑。

ReactUpdates.ReactReconcileTransaction

ReactReconcileTransaction 属性是全局依赖注入通过ReactUpdates.injection.injectReconcileTransaction方法完成赋值操作。

该属性翻译为中文就是 ‘React 调和事务’。这边reconcile调和调节得意思,实质上就是一个过程,state改变导致VDom改变,然后计算真实Dom的过程。这边用到了Transaction将在下一篇博客进行讲解。并将React中得各个Transaction衍生而来得自定义Transaction做一个总结。

ReactUpdates.batchedUpdates

batchedUpdates 翻译为中文就是‘批量更新’,大家都知道在React中,setState方法是批处理的。这也是为什么React会渲染很快的原因,内部的更新进行批处理操作,避免不必要的渲染。

function batchedUpdates(callback, a, b, c, d, e) {
  ensureInjected();
  return batchingStrategy.batchedUpdates(callback, a, b, c, d, e);
}
复制代码

这边首先执行一个ensureInjected()方法,目的是确保之前依赖注入的两个变量都完成了注入。

function ensureInjected() {
  !(ReactUpdates.ReactReconcileTransaction && batchingStrategy)
    ? process.env.NODE_ENV !== 'production' ? invariant(false, 'ReactUpdates: must inject a reconcile transaction class and batching strategy')
    : _prodInvariant('123') : void 0;
}
复制代码

顺便一提,这边有一个小东西 void 0,在我的博客网站有做过说明,附上传送门void (0) ??? undefined

那么回到正题,这个方法本质上是调用的batchingStrategy.batchedUpdates(callback, a, b, c, d, e),这边的batchingStrategy就是一开始依赖注入的ReactDefaultBatchingStrategy,是React的默认批处理策略。下一片博客的主题就是这个。

ReactUpdates.flushBatchedUpdates

这边提到一个对象叫dirtyComponents,因为还没有用到,所以这边只是大概的描述一下,在你的React中,当你调用setState时,React会将你传入的值压倒一个队列中,然后将该组件标记为一个dirtyComponent,最后会回过头来查看这个更新队列,遍历队列计算出最终的值,然后进行渲染,这样就达到了批处理的操作,避免了大量不必要的渲染。

那么这边用到了一个ReactUpdatesFlushTransaction ‘更新刷新事务’

var flushBatchedUpdates = function () {
  // ReactUpdatesFlushTransaction's wrappers will clear the dirtyComponents
  // array and perform any updates enqueued by mount-ready handlers (i.e.,
  // componentDidUpdate) but we need to check here too in order to catch
  // updates enqueued by setState callbacks and asap calls.
  while (dirtyComponents.length || asapEnqueued) {
    if (dirtyComponents.length) {
      var transaction = ReactUpdatesFlushTransaction.getPooled();
      transaction.perform(runBatchedUpdates, null, transaction);
      ReactUpdatesFlushTransaction.release(transaction);
    }

    if (asapEnqueued) {
      asapEnqueued = false;
      var queue = asapCallbackQueue;
      asapCallbackQueue = CallbackQueue.getPooled();
      queue.notifyAll();
      CallbackQueue.release(queue);
    }
  }
};
复制代码

ReactUpdates.asap

不知道是个啥东西。。。

/**
 * Enqueue a callback to be run at the end of the current batching cycle. Throws
 * if no updates are currently being performed.
 * 在当前批处理周期结束时运行回调。如果当前没有执行更新,则引发。
 */
function asap(callback, context) {
  invariant(batchingStrategy.isBatchingUpdates, "ReactUpdates.asap: Can't enqueue an asap callback in a context where" + 'updates are not being batched.');
  asapCallbackQueue.enqueue(callback, context);
  asapEnqueued = true;
}
复制代码

总结

那么这边是执行ReactUpdates.batchedUpdates,它实质上是执行的ReactDefaultBatchingStrategy.batchedUpdates,这样我们的渲染流程到此就又加上一笔。

image_1dlqt4cau18a5109u1rrf19dd1l39.png-68.1kB

本篇留坑

Transaction 及尤其衍生出的自定义事务

ReactDefaultBatchingStrategy

下篇博客:ReactDefaultBatchingStrategy