成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

從源碼全面剖析 React 組件更新機(jī)制

摘要:把組件看作狀態(tài)機(jī)有限狀態(tài)機(jī)使用來(lái)控制本地狀態(tài)使用來(lái)傳遞狀態(tài)前面我們探討了如何映射狀態(tài)到上初始渲染那么接下來(lái)我們談?wù)剷r(shí)如何同步狀態(tài)到上的也就是是如何更新組件的是如何對(duì)比出頁(yè)面變化最小的部分這篇文章會(huì)為你解答這些問(wèn)題在這之前你已經(jīng)了解了版本內(nèi)

React 把組件看作狀態(tài)機(jī)(有限狀態(tài)機(jī)), 使用state來(lái)控制本地狀態(tài), 使用props來(lái)傳遞狀態(tài). 前面我們探討了 React 如何映射狀態(tài)到 UI 上(初始渲染), 那么接下來(lái)我們談?wù)?React 時(shí)如何同步狀態(tài)到 UI 上的, 也就是:

React 是如何更新組件的?

React 是如何對(duì)比出頁(yè)面變化最小的部分?

這篇文章會(huì)為你解答這些問(wèn)題.

在這之前

你已經(jīng)了解了React (15-stable版本)內(nèi)部的一些基本概念, 包括不同類(lèi)型的組件實(shí)例、mount過(guò)程、事務(wù)、批量更新的大致過(guò)程(還沒(méi)有? 不用擔(dān)心, 為你準(zhǔn)備好了從源碼看組件初始渲染、接著從源碼看組件初始渲染);

準(zhǔn)備一個(gè)demo, 調(diào)試源碼, 以便更好理解;

Keep calm and make a big deal !

React 是如何更新組件的?
TL;DR

依靠事務(wù)進(jìn)行批量更新;

一次batch(批量)的生命周期就是從ReactDefaultBatchingStrategy事務(wù)perform之前(調(diào)用ReactUpdates.batchUpdates)到這個(gè)事務(wù)的最后一個(gè)close方法調(diào)用后結(jié)束;

事務(wù)啟動(dòng)后, 遇到 setState 則將 partial state 存到組件實(shí)例的_pendingStateQueue上, 然后將這個(gè)組件存到dirtyComponents 數(shù)組中, 等到 ReactDefaultBatchingStrategy事務(wù)結(jié)束時(shí)調(diào)用runBatchedUpdates批量更新所有組件;

組件的更新是遞歸的, 三種不同類(lèi)型的組件都有自己的updateComponent方法來(lái)決定自己的組件如何更新, 其中 ReactDOMComponent 會(huì)采用diff算法對(duì)比子元素中最小的變化, 再批量處理.

這個(gè)更新過(guò)程像是一套流程, 無(wú)論你通過(guò)setState(或者replaceState)還是新的props去更新一個(gè)組件, 都會(huì)起作用.

那么具體是什么?

讓我們從這套更新流程的開(kāi)始部分講起...

調(diào)用 setState 之前

首先, 開(kāi)始一次batch的入口是在ReactDefaultBatchingStrategy里, 調(diào)用里面的batchedUpdates便可以開(kāi)啟一次batch:

// 批處理策略
var ReactDefaultBatchingStrategy = {
  isBatchingUpdates: false, 
  batchedUpdates: function(callback, a, b, c, d, e) {
    var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
    ReactDefaultBatchingStrategy.isBatchingUpdates = true; // 開(kāi)啟一次batch

    if (alreadyBatchingUpdates) {
      return callback(a, b, c, d, e);
    } else {
      // 啟動(dòng)事務(wù), 將callback放進(jìn)事務(wù)里執(zhí)行
      return transaction.perform(callback, null, a, b, c, d, e);  
    }
  },
};

在 React 中, 調(diào)用batchedUpdates有很多地方, 與更新流程相關(guān)的如下

// ReactMount.js
ReactUpdates.batchedUpdates(
      batchedMountComponentIntoNode,  // 負(fù)責(zé)初始渲染
      componentInstance,
      container,
      shouldReuseMarkup,
      context,
);

// ReactEventListener.js
dispatchEvent: function(topLevelType, nativeEvent) {
    ...
    try {
      ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);  // 處理事件
    } finally {
      TopLevelCallbackBookKeeping.release(bookKeeping);
    }
},

第一種情況, React 在首次渲染組件的時(shí)候會(huì)調(diào)用batchedUpdates, 然后開(kāi)始渲染組件. 那么為什么要在這個(gè)時(shí)候啟動(dòng)一次batch呢? 不是因?yàn)橐坎迦? 因?yàn)椴迦脒^(guò)程是遞歸的, 而是因?yàn)榻M件在渲染的過(guò)程中, 會(huì)依順序調(diào)用各種生命周期函數(shù), 開(kāi)發(fā)者很可能在生命周期函數(shù)中(如componentWillMount或者componentDidMount)調(diào)用setState. 因此, 開(kāi)啟一次batch就是要存儲(chǔ)更新(放入dirtyComponents), 然后在事務(wù)結(jié)束時(shí)批量更新. 這樣以來(lái), 在初始渲染流程中, 任何setState都會(huì)生效, 用戶(hù)看到的始終是最新的狀態(tài).

第二種情況, 如果你在HTML元素上或者組件上綁定了事件, 那么你有可能在事件的監(jiān)聽(tīng)函數(shù)中調(diào)用setState, 因此, 同樣為了存儲(chǔ)更新(放入dirtyComponents), 需要啟動(dòng)批量更新策略. 在回調(diào)函數(shù)被調(diào)用之前, React事件系統(tǒng)中的dispatchEvent函數(shù)負(fù)責(zé)事件的分發(fā), 在dispatchEvent中啟動(dòng)了事務(wù), 開(kāi)啟了一次batch, 隨后調(diào)用了回調(diào)函數(shù). 這樣一來(lái), 在事件的監(jiān)聽(tīng)函數(shù)中調(diào)用的setState就會(huì)生效.

也就是說(shuō), 任何可能調(diào)用 setState 的地方, 在調(diào)用之前, React 都會(huì)啟動(dòng)批量更新策略以提前應(yīng)對(duì)可能的setState

那么調(diào)用 batchedUpdates 后發(fā)生了什么?

React 調(diào)用batchedUpdates時(shí)會(huì)傳進(jìn)去一個(gè)函數(shù), batchedUpdates會(huì)啟動(dòng)ReactDefaultBatchingStrategyTransaction事務(wù), 這個(gè)函數(shù)就會(huì)被放在事務(wù)里執(zhí)行:

// ReactDefaultBatchingStrategy.js
var transaction = new ReactDefaultBatchingStrategyTransaction(); // 實(shí)例化事務(wù)
var ReactDefaultBatchingStrategy = {
  ...
  batchedUpdates: function(callback, a, b, c, d, e) {
    ...
      return transaction.perform(callback, null, a, b, c, d, e);  // 將callback放進(jìn)事務(wù)里執(zhí)行
       ...
};

ReactDefaultBatchingStrategyTransaction這個(gè)事務(wù)控制了批量策略的生命周期:

// ReactDefaultBatchingStrategy.js
var FLUSH_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates),  // 批量更新
};
var RESET_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: function() {
    ReactDefaultBatchingStrategy.isBatchingUpdates = false;  // 結(jié)束本次batch
  },
};
var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];

無(wú)論你傳進(jìn)去的函數(shù)是什么, 無(wú)論這個(gè)函數(shù)后續(xù)會(huì)做什么, 都會(huì)在執(zhí)行完后調(diào)用上面事務(wù)的close方法, 先調(diào)用flushBatchedUpdates批量更新, 再結(jié)束本次batch.

調(diào)用 setState 后發(fā)生了什么
// ReactBaseClasses.js :
ReactComponent.prototype.setState = function(partialState, callback) {
  this.updater.enqueueSetState(this, partialState);
  if (callback) {
    this.updater.enqueueCallback(this, callback, "setState");
  }
};

// => ReactUpdateQueue.js:
enqueueSetState: function(publicInstance, partialState) {
    // 根據(jù) this.setState 中的 this 拿到內(nèi)部實(shí)例, 也就是組件實(shí)例
    var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, "setState");
    // 取得組件實(shí)例的_pendingStateQueue
    var queue =
      internalInstance._pendingStateQueue ||
      (internalInstance._pendingStateQueue = []);
    // 將partial state存到_pendingStateQueue
    queue.push(partialState);
    // 調(diào)用enqueueUpdate
    enqueueUpdate(internalInstance);
 }

// => ReactUpdate.js:
function enqueueUpdate(component) {
  ensureInjected(); // 注入默認(rèn)策略
    
    // 如果沒(méi)有開(kāi)啟batch(或當(dāng)前batch已結(jié)束)就開(kāi)啟一次batch再執(zhí)行, 這通常發(fā)生在異步回調(diào)中調(diào)用 setState      // 的情況
  if (!batchingStrategy.isBatchingUpdates) {
    batchingStrategy.batchedUpdates(enqueueUpdate, component);
    return;
  }
    // 如果batch已經(jīng)開(kāi)啟就存儲(chǔ)更新
  dirtyComponents.push(component);
  if (component._updateBatchNumber == null) {
    component._updateBatchNumber = updateBatchNumber + 1;
  }
}

也就是說(shuō), 調(diào)用 setState 會(huì)首先拿到內(nèi)部組件實(shí)例, 然后把要更新的partial state存到其_pendingStateQueue中, 然后標(biāo)記當(dāng)前組件為dirtyComponent, 存到dirtyComponents數(shù)組中. 然后就接著繼續(xù)做下面的事情了, 并沒(méi)有立即更新, 這是因?yàn)榻酉聛?lái)要執(zhí)行的代碼里有可能還會(huì)調(diào)用 setState, 因此只做存儲(chǔ)處理.

什么時(shí)候批量更新?

首先, 一個(gè)事務(wù)在執(zhí)行的時(shí)候(包括initialize、perform、close階段), 任何一階段都有可能調(diào)用一系列函數(shù), 并且開(kāi)啟了另一些事務(wù). 那么只有等后續(xù)開(kāi)啟的事務(wù)執(zhí)行完, 之前開(kāi)啟的事務(wù)才繼續(xù)執(zhí)行. 下圖是我們剛才所說(shuō)的第一種情況, 在初始渲染組件期間 setState 后, React 啟動(dòng)的各種事務(wù)和執(zhí)行的順序:

從圖中可以看到, 批量更新是在ReactDefaultBatchingStrategyTransaction事務(wù)的close階段, 在flushBatchedUpdates函數(shù)中啟動(dòng)了ReactUpdatesFlushTransaction事務(wù)負(fù)責(zé)批量更新.

怎么批量更新的?
開(kāi)啟批量更新事務(wù)、批量處理callback

我們接著看flushBatchedUpdates函數(shù), 在ReactUpdates.js中

var flushBatchedUpdates = function () {
  // 啟動(dòng)批量更新事務(wù)
  while (dirtyComponents.length || asapEnqueued) {
    if (dirtyComponents.length) {
      var transaction = ReactUpdatesFlushTransaction.getPooled();
      transaction.perform(runBatchedUpdates, null, transaction);
      ReactUpdatesFlushTransaction.release(transaction);
    }
// 批量處理callback
    if (asapEnqueued) {
      asapEnqueued = false;
      var queue = asapCallbackQueue;
      asapCallbackQueue = CallbackQueue.getPooled();
      queue.notifyAll();
      CallbackQueue.release(queue);
    }
  }
};
遍歷dirtyComponents

flushBatchedUpdates啟動(dòng)了一個(gè)更新事務(wù), 這個(gè)事務(wù)執(zhí)行了runBatchedUpdates進(jìn)行批量更新:

// ReactUpdates.js
function runBatchedUpdates(transaction) {
  var len = transaction.dirtyComponentsLength;
  // 排序保證父組件優(yōu)先于子組件更新
  dirtyComponents.sort(mountOrderComparator);

  // 代表批量更新的次數(shù), 保證每個(gè)組件只更新一次
  updateBatchNumber++;
  // 遍歷 dirtyComponents
  for (var i = 0; i < len; i++) {
    var component = dirtyComponents[i];
      
    var callbacks = component._pendingCallbacks;
    component._pendingCallbacks = null;
    ...
    // 執(zhí)行更新
    ReactReconciler.performUpdateIfNecessary(
      component,
      transaction.reconcileTransaction,
      updateBatchNumber,
    );
    ...
    // 存儲(chǔ) callback以便后續(xù)按順序調(diào)用
    if (callbacks) {
      for (var j = 0; j < callbacks.length; j++) {
        transaction.callbackQueue.enqueue(
          callbacks[j],
          component.getPublicInstance(),
        );
      }
    }
  }
}

前面 setState 后將組件推入了dirtyComponents, 現(xiàn)在就是要遍歷dirtyComponents數(shù)組進(jìn)行更新了.

根據(jù)不同情況執(zhí)行更新

ReactReconciler會(huì)調(diào)用組件實(shí)例的performUpdateIfNecessary. 如果接收了props, 就會(huì)調(diào)用此組件的receiveComponent, 再在里面調(diào)用updateComponent更新組件; 如果沒(méi)有接受props, 但是有新的要更新的狀態(tài)(_pendingStateQueue不為空)就會(huì)直接調(diào)用updateComponent來(lái)更新:

// ReactCompositeComponent.js
performUpdateIfNecessary: function (transaction) {
    if (this._pendingElement != null) {
        ReactReconciler.receiveComponent(this, this._pendingElement, transaction,                 this._context);
    } else if (this._pendingStateQueue !== null || this._pendingForceUpdate) {
        this.updateComponent(transaction, this._currentElement, this._currentElement,             this._context, this._context);
    } else {
        this._updateBatchNumber = null;
    }
}
調(diào)用組件實(shí)例的updateComponent

接下里就是重頭戲updateComponent了, 它決定了組件如果更新自己和它的后代們. 需要特別注意的是, React 內(nèi)部三種不同的組件類(lèi)型, 每種組件都有自己的updateComponent, 有不同的行為.

對(duì)于 ReactCompositeComponent (矢量圖):

updateComponent所做的事情 :

調(diào)用此層級(jí)組件的一系列生命周期函數(shù), 并且在合適的時(shí)機(jī)更新props、state、context;

re-render, 與之前 render 的 element 比較, 如果兩者key && element.type 相等, 則進(jìn)入下一層進(jìn)行更新; 如果不等, 直接移除重新mount

對(duì)于 ReactDOMComponent:

updateComponent所做的事情 :

更新這一層級(jí)DOM元素屬性;

更新子元素, 調(diào)用 ReactMultiChild 的 updateChildren, 對(duì)比前后變化、標(biāo)記變化類(lèi)型、存到updates中(diff算法主要部分);

批量處理updates

對(duì)于 ReactDOMTextComponent :

上面只是每個(gè)組件自己更新的過(guò)程, 那么 React 是如何一次性更新所有組件的 ? 答案是遞歸.

遞歸調(diào)用組件的updateComponent

觀察 ReactCompositeComponent 和 ReactDOMComponent 的更新流程, 我們發(fā)現(xiàn) React 每次走到一個(gè)組件更新過(guò)程的最后部分, 都會(huì)有一個(gè)判斷 : 如果 nextELement 和 prevElement key 和 type 相等, 就會(huì)調(diào)用receiveComponent. receiveComponentupdateComponent一樣, 每種組件都有一個(gè), 作用就相當(dāng)于updateComponent 接受了新 props 的版本. 而這里調(diào)用的就是子元素的receiveComponent, 進(jìn)而進(jìn)行子元素的更新, 于是就形成了遞歸更新、遞歸diff. 因此, 整個(gè)流程就像這樣(矢量圖) :

這種更新完一級(jí)、diff完一級(jí)再進(jìn)入下一級(jí)的過(guò)程保證 React 只遍歷一次組件樹(shù)就能完成更新, 但代價(jià)就是只要前后 render 出元素的 type 和 key 有一個(gè)不同就刪除重造, 因此, React 建議頁(yè)面要盡量保持穩(wěn)定的結(jié)構(gòu).

React 是如何對(duì)比出頁(yè)面變化最小的部分?

你可能會(huì)說(shuō) React 用 virtual DOM 表示了頁(yè)面結(jié)構(gòu), 每次更新, React 都會(huì)re-render出新的 virtual DOM, 再通過(guò) diff 算法對(duì)比出前后變化, 最后批量更新. 沒(méi)錯(cuò), 很好, 這就是大致過(guò)程, 但這里存在著一些隱藏的深層問(wèn)題值得探討 :

React 是如何用 virtual DOM 表示了頁(yè)面結(jié)構(gòu), 從而使任何頁(yè)面變化都能被 diff 出來(lái)?

React 是如何 diff 出頁(yè)面變化最小的部分?

React 如何表示頁(yè)面結(jié)構(gòu)
class C extends React.Component {
    render () {
        return (
            
"dscsdcsd" console.log(e)}>{this.state.val}
) } } // virtual DOM(React element) { $$typeof: Symbol(react.element) key: null props: { // props 代表元素上的所有屬性, 有children屬性, 描述子組件, 同樣是元素 children: [ ""dscsdcsd"", {$$typeof: Symbol(react.element), type: "i", key: null, ref: null, props: {…}, …}, {$$typeof: Symbol(react.element), type: class Children, props: {…}, …} ] className: "container" } ref: null type: "div" _owner: ReactCompositeComponentWrapper {...} // class C 實(shí)例化后的對(duì)象 _store: {validated: false} _self: null _source: null }

每個(gè)標(biāo)簽, 無(wú)論是DOM元素還是自定義組件, 都會(huì)有 key、type、props、ref 等屬性.

key 代表元素唯一id值, 意味著只要id改變, 就算前后元素種類(lèi)相同, 元素也肯定不一樣了;

type 代表元素種類(lèi), 有 function(空的wrapper)、class(自定義類(lèi))、string(具體的DOM元素名稱(chēng))類(lèi)型, 與key一樣, 只要改變, 元素肯定不一樣;

props 是元素的屬性, 任何寫(xiě)在標(biāo)簽上的屬性(如className="container")都會(huì)被存在這里, 如果這個(gè)元素有子元素(包括文本內(nèi)容), props就會(huì)有children屬性, 存儲(chǔ)子元素; children屬性是遞歸插入、遞歸更新的依據(jù);

也就是說(shuō), 如果元素唯一標(biāo)識(shí)符或者類(lèi)別或者屬性有變化, 那么它們r(jià)e-render后對(duì)應(yīng)的 key、type 和props里面的屬性也會(huì)改變, 前后一對(duì)比即可找出變化. 綜上來(lái)看, React 這么表示頁(yè)面結(jié)構(gòu)確實(shí)能夠反映前后所有變化.

那么 React 是如何 diff 的?

React diff 每次只對(duì)同一層級(jí)的節(jié)點(diǎn)進(jìn)行比對(duì) :

上圖的數(shù)字表示遍歷更新的次序.

從父節(jié)點(diǎn)開(kāi)始, 每一層 diff 包括兩個(gè)地方

element diff—— 前后 render 出來(lái)的 element 的對(duì)比, 這個(gè)對(duì)比是為了找出前后節(jié)點(diǎn)是不是同一節(jié)點(diǎn), 會(huì)對(duì)比前后render出來(lái)的元素它們的 key 和 type. element diff 包括兩個(gè)地方, 組件頂層DOM元素對(duì)比和子元素的對(duì)比:

組件頂層DOM元素對(duì)比 :

// ReactCompositeComponent.js/updateComponent => _updateRenderedComponent
_updateRenderedComponent: function(transaction, context) {
    // re-render 出element
    var nextRenderedElement = this._renderValidatedComponent();
    // 對(duì)比前后變化
    if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) {
      // 如果 key && type 沒(méi)變進(jìn)行下一級(jí)更新
      ReactReconciler.receiveComponent(...);
    } else {
      // 如果變了移除重造
      ReactReconciler.unmountComponent(prevComponentInstance, false);
      ...
      var child = this._instantiateReactComponent(...);
  
      var nextMarkup = ReactReconciler.mountComponent(...);
      this._replaceNodeWithMarkup(...);
    }
}

子元素的對(duì)比:

// ReactChildReconciler.js
updateChildren: function(...) {
    ...
    for (name in nextChildren) {  // 遍歷 re-render 出的elements
      ...
      if (
        prevChild != null &&
        shouldUpdateReactComponent(prevElement, nextElement)
      ) {
        // 如果key && type 沒(méi)變進(jìn)行下一級(jí)更新
        ReactReconciler.receiveComponent(...);  
        nextChildren[name] = prevChild;  // 更新完放入 nextChildren, 注意放入的是組件實(shí)例
      } else {
        // 如果變了則移除重建                               
        if (prevChild) {
          removedNodes[name] = ReactReconciler.getHostNode(prevChild);
          ReactReconciler.unmountComponent(prevChild, false);
        }
        var nextChildInstance = instantiateReactComponent(nextElement, true);
        nextChildren[name] = nextChildInstance;
          
        var nextChildMountImage = ReactReconciler.mountComponent(...);
        mountImages.push(nextChildMountImage);
      }
    }
    // 再除掉 prevChildren 里有, nextChildren 里沒(méi)有的組件
    for (name in prevChildren) {
      if (
        prevChildren.hasOwnProperty(name) &&
        !(nextChildren && nextChildren.hasOwnProperty(name))
      ) {
        prevChild = prevChildren[name];
        removedNodes[name] = ReactReconciler.getHostNode(prevChild);
        ReactReconciler.unmountComponent(prevChild, false);
      }
    }
  },

shouldComponentUpdate 函數(shù):

function shouldUpdateReactComponent(prevElement, nextElement) {
  
  var prevEmpty = prevElement === null || prevElement === false;
  var nextEmpty = nextElement === null || nextElement === false;
  if (prevEmpty || nextEmpty) {
    return prevEmpty === nextEmpty;
  }

  var prevType = typeof prevElement;
  var nextType = typeof nextElement;
  // 如果前后變化都是字符串、數(shù)字類(lèi)型的則允許更新
  if (prevType === "string" || prevType === "number") {
    return nextType === "string" || nextType === "number";
  } else {
    // 否則檢查 type && key
    return (
      nextType === "object" &&
      prevElement.type === nextElement.type &&
      prevElement.key === nextElement.key
    );
  }
}

element diff 檢測(cè) type && key 都沒(méi)變時(shí)會(huì)進(jìn)入下一級(jí)更新, 如果變化則直接移除重造新元素, 然后遍歷同級(jí)的下一個(gè).

subtree diff ——組件頂層DOM元素包裹的所有子元素(也就是props.children里的元素)與之前版本的對(duì)比, 這個(gè)對(duì)比是為了找出同級(jí)所有子節(jié)點(diǎn)的變化, 包括移除、新建、同級(jí)范圍的移動(dòng);

// ReactMultiChild.js
_updateChildren: function(...) {
      var prevChildren = this._renderedChildren;
      var removedNodes = {};
      var mountImages = [];
      // 拿到更新后子組件實(shí)例
      var nextChildren = this._reconcilerUpdateChildren();
      ...
      // 遍歷子組件實(shí)例
      for (name in nextChildren) {
           ...
        var prevChild = prevChildren && prevChildren[name];
        var nextChild = nextChildren[name];
        // 因?yàn)樽咏M件的更新是在原組件實(shí)例上更改的, 因此與之前的組件作引用比較即可判斷
        if (prevChild === nextChild) {
            // 發(fā)生了移動(dòng)
          updates = enqueue(
            updates,
            this.moveChild(prevChild, lastPlacedNode, nextIndex, lastIndex),
          );
          lastIndex = Math.max(prevChild._mountIndex, lastIndex);
          prevChild._mountIndex = nextIndex;
        } else {
          ...
          // 有新的組件
          updates = enqueue(
            updates,
            this._mountChildAtIndex(
              nextChild,
              mountImages[nextMountIndex],
              lastPlacedNode,
              nextIndex,
              transaction,
              context,
            ),
          );
          nextMountIndex++;
        }
        nextIndex++;
        lastPlacedNode = ReactReconciler.getHostNode(nextChild);
      }
      // Remove children that are no longer present.
      for (name in removedNodes) {
          // removedNodes 記錄了所有的移除節(jié)點(diǎn)
        if (removedNodes.hasOwnProperty(name)) {
          updates = enqueue(
            updates,
            this._unmountChild(prevChildren[name], removedNodes[name]),
          );
        }
      }
      if (updates) {
        processQueue(this, updates); // 批量處理
      }
      this._renderedChildren = nextChildren;
    },

React 會(huì)將同一層級(jí)的變化標(biāo)記, 如 MOVE_EXISTING、REMOVE_NODE、TEXT_CONTENT、INSERT_MARKUP 等, 統(tǒng)一放到 updates 數(shù)組中然后批量處理.

And that‘s it !

React 是一個(gè)激動(dòng)人心的庫(kù), 它給我們帶來(lái)了前所未有的開(kāi)發(fā)體驗(yàn), 但當(dāng)我們沉浸在使用 React 快速實(shí)現(xiàn)需求的喜悅中時(shí), 有必要去探究?jī)蓚€(gè)問(wèn)題 : Why and How?

為什么 React 會(huì)如此流行, 原因是什么? 組件化、快速、足夠簡(jiǎn)單、all in js、容易擴(kuò)展、生態(tài)豐富、社區(qū)強(qiáng)大...

React 反映了哪些思想/理念/思路 ? 狀態(tài)機(jī)、webComponents、virtual DOM、virtual stack、異步渲染、多端渲染、單向數(shù)據(jù)流、反應(yīng)式更新、函數(shù)式編程...

React 這些理念/思路受什么啟發(fā) ? 怎么想到的 ? 又怎么實(shí)現(xiàn)的? ...

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/94338.html

相關(guān)文章

  • React-Redux源碼剖析

    摘要:為了能夠更好的使用這個(gè)工具,今天就對(duì)它進(jìn)行一下源碼剖析。它內(nèi)部的關(guān)鍵代碼是在不指定的時(shí)候等于,這就意味著的源碼剖析到此結(jié)束,謝謝觀看當(dāng)然如果指定了剖析就還得繼續(xù)。好了,源碼剖析到此結(jié)束,謝謝觀看 React-Redux是用在連接React和Redux上的。如果你想同時(shí)用這兩個(gè)框架,那么React-Redux基本就是必須的了。為了能夠更好的使用這個(gè)工具,今天就對(duì)它進(jìn)行一下源碼剖析。 Pr...

    Shimmer 評(píng)論0 收藏0
  • React 源碼剖析系列 - 不可思議的 react diff

    摘要:目前,前端領(lǐng)域中勢(shì)頭正盛,使用者眾多卻少有能夠深入剖析內(nèi)部實(shí)現(xiàn)機(jī)制和原理。當(dāng)發(fā)現(xiàn)節(jié)點(diǎn)已經(jīng)不存在,則該節(jié)點(diǎn)及其子節(jié)點(diǎn)會(huì)被完全刪除掉,不會(huì)用于進(jìn)一步的比較。 目前,前端領(lǐng)域中 React 勢(shì)頭正盛,使用者眾多卻少有能夠深入剖析內(nèi)部實(shí)現(xiàn)機(jī)制和原理。本系列文章希望通過(guò)剖析 React 源碼,理解其內(nèi)部的實(shí)現(xiàn)原理,知其然更要知其所以然。 React diff 作為 Virtual DOM 的加速...

    shuibo 評(píng)論0 收藏0
  • SegmentFault 技術(shù)周刊 Vol.4 - 這份 Android 有點(diǎn)甜

    摘要:閱讀本期周刊,你將快速入門(mén),開(kāi)啟甜蜜之旅。然則的原理負(fù)責(zé)發(fā)送以及處理消息,創(chuàng)建消息隊(duì)列并不斷從隊(duì)列中取出消息交給,則用于保存消息。 showImg(/img/bVCN99?w=900&h=385); 2016 年 8 月,Android 7.0 Nougat(牛軋?zhí)牵┱桨l(fā)布,那么問(wèn)題來(lái)了,你 Marshmallow 了么(? -? ?) Cupcake、Donut、Gingerbre...

    jay_tian 評(píng)論0 收藏0
  • 剖析 React 源碼:render 流程(一)

    摘要:大家可以看到是構(gòu)造函數(shù)構(gòu)造出來(lái)的,并且內(nèi)部有一個(gè)對(duì)象,這個(gè)對(duì)象是本文接下來(lái)要重點(diǎn)介紹的對(duì)象,接下來(lái)我們就來(lái)一窺究竟吧。在構(gòu)造函數(shù)內(nèi)部就進(jìn)行了一步操作,那就是創(chuàng)建了一個(gè)對(duì)象,并掛載到了上。下一篇文章還是流程相關(guān)的內(nèi)容。這是我的剖析 React 源碼的第二篇文章,如果你沒(méi)有閱讀過(guò)之前的文章,請(qǐng)務(wù)必先閱讀一下 第一篇文章 中提到的一些注意事項(xiàng),能幫助你更好地閱讀源碼。 文章相關(guān)資料 React ...

    hiYoHoo 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<