拆解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--

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容