拆解setState[二][一源看世界][之React]

上一章节《拆解SetState[一][一源看世界][之React]》找到了updater的真面目为ReactUpdateQueue,接下来就聊聊enqueueSetStateenqueueCallback

enqueueSetState:
  enqueueSetState: function(publicInstance, partialState) {
    ...

    var internalInstance = getInternalInstanceReadyForUpdate(
      publicInstance,
      'setState'
    );

    if (!internalInstance) {
      return;
    }

    var queue =
      internalInstance._pendingStateQueue ||
      (internalInstance._pendingStateQueue = []);
    queue.push(partialState);

    enqueueUpdate(internalInstance);
  },
enqueueCallback
  enqueueCallback: function(publicInstance, callback, callerName) {
    ReactUpdateQueue.validateCallback(callback, callerName);
    var internalInstance = getInternalInstanceReadyForUpdate(publicInstance);

    ...
    if (!internalInstance) {
      return null;
    }

    if (internalInstance._pendingCallbacks) {
      internalInstance._pendingCallbacks.push(callback);
    } else {
      internalInstance._pendingCallbacks = [callback];
    }
    ...
    enqueueUpdate(internalInstance);
  },

可以看出两个方法都引用了enqueueUpdate方法。逻辑如下:

  • 通过getInternalInstanceReadyForUpdate方法取得internalInstance,这个实例是ReactCompositeComponentWrapper实例(源码在instantiateReactComponent.js)
  • internalInstance进行修改
    enqueueSetState是将state的更改放进_pendingStateQueue队列;enqueueCallback是将callback回调方法放进_pendingCallbacks队列
  • 最后调用enqueueUpdate方法刷新所做的更改

我们去看一下enqueueUpdate是怎么完成任务的吧

function enqueueUpdate(internalInstance) {
  ReactUpdates.enqueueUpdate(internalInstance);
}

这里又有点令人费解!为什么这里的ReactUpdates是直接引用而不是注入呢?那是因为ReactUpdates提供了公用的方法,而它的依赖才是注入的。继续看看ReactUpdates是怎么工作的。

function enqueueUpdate(component) {
  ensureInjected();

  ...

  if (!batchingStrategy.isBatchingUpdates) {
    batchingStrategy.batchedUpdates(enqueueUpdate, component);
    return;
  }

  dirtyComponents.push(component);
  if (component._updateBatchNumber == null) {
    component._updateBatchNumber = updateBatchNumber + 1;
  }
}

ensureInjected的实现逻辑可得知ReactUpdates的依赖是ReactUpdates.ReactReconcileTransactionbatchingStrategy

batchingStrategy是一种React如何批量更新的策略。当前只有一种策略,叫ReactDefaultBatchingStrategy

ReactReconcileTransaction是跟环境相关的负责更新后的一些状态处理工作 - 比如DOM,它负责修复更新后会导致文本选择状态的丢失问题,在reconciliation期间禁止事件和将生命周期方法加入队列,具体可以看源码中它的Wrappers

var TRANSACTION_WRAPPERS = [
  SELECTION_RESTORATION,
  EVENT_SUPPRESSION,
  ON_DOM_READY_QUEUEING,
];

上面代码的基本逻辑是:batchingStrategy有一个属性告诉你当前是否处于事务之中,如果不是,enqueueUpdate将它自己放入事务去执行;反之,将component(ReactCompositeComponentWrapper实例)放入dirtyComponents数组中。


那接下来又会发生什么事呢?感觉水很深有木有。。。

为了弄清楚,我们得知道batchingStrategy是在哪里注入,注入了哪个对象,又经过千山万水,在ReactDOM中找到线索(ReactNative也有类似的注入)

ReactDefaultInjection.inject();

而在ReactDefaultInjection源码中,可以清楚地看到注入的对象了,即ReactDefaultBatchingStrategy

ReactInjection.Updates.injectReconcileTransaction(
  ReactReconcileTransaction
);
ReactInjection.Updates.injectBatchingStrategy(
  ReactDefaultBatchingStrategy
);

研究一下ReactDefaultBatchingStrategy.batchedUpdates方法,发现这是个事务方法,使用ReactDefaultBatchingStrategyTransaction这个事务,对于事务,我们就只需要看Wrappers了,Wrappers有两个:

var RESET_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: function() {
    ReactDefaultBatchingStrategy.isBatchingUpdates = false;
  },
};

var FLUSH_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates),
};
  • RESET_BATCHED_UPDATES - 仅仅在事务结束时清理一下标识
  • FLUSH_BATCHED_UPDATES - 在事务结束时执行flushBatchedUpdates方法,这个方法就是state更新的核心代码了。

那我们以热烈的掌声欢迎state更新中最重要最核心的主角上场:flushBatchedUpdates

var flushBatchedUpdates = function() {
  ...
  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);
    }
  }
  ...
};

这里又有一种事务ReactUpdatesFlushTransaction,它主要负责“捕获”任何在执行flushBatchedUpdates后的pending中的更新。另外可以看出,这个事务是通过getPooled ()(事务实例对象是提前准备好而不是实时创建 - 这样的好处是可以避免不必要的GC)取得的。

还有一个概念asap updates,在接下来会好好讲讲。但现在,最需要的是休息,休息,好烧脑,敬请期待下一章节,不见不散。


最后,期待吐槽,期待指教!!!

--EOF--

你可能感兴趣的:(拆解setState[二][一源看世界][之React])