React-原始碼解析-setState執行機制

mazy發表於2018-06-21

image

以程式碼註釋的方式整理setState的執行流程


用class 的方式建立React元件,需要繼承React.Component,而setState()方法就繫結在他的原型上面


  • 發現setState()上有兩個引數
  • 第一個引數是需要修改的state物件,或者是函式(preState:更新前的sate,props:當前的屬性)
  • 第二個引數是修改完state執行的回撥函式

ReactComponent.prototype.isReactComponent = {};
ReactComponent.prototype.setState = function(partialState, callback) {
  //這裡的this.updater就是ReactUpdateQueue,--- this是元件的例項
  this.updater.enqueueSetState(this, partialState);
  if (callback) {
    this.updater.enqueueCallback(this, callback, 'setState');
  }
};
複製程式碼

  • 呼叫了this.updater.enqueueSetState(this, partialState);
  • 主要是初始化更新佇列,並將需要修改的state加入到更新佇列中
enqueueSetState: function(publicInstance, partialState) {
    //獲取當前例項
    //例項中有兩個非常重要的屬性
    //_pendingStateQueue(待更新佇列) 與 _pendingCallbacks(更新回撥佇列)
    var internalInstance = getInternalInstanceReadyForUpdate( publicInstance, 'setState',  );
    if (!internalInstance) {
      return;
    }
    //初始化待更新佇列
    var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
    queue.push(partialState);
    enqueueUpdate(internalInstance);
},
複製程式碼

  • 呼叫enqueueUpdate(internalInstance)時呼叫 ReactUpdates.enqueueUpdate(internalInstance);
function enqueueUpdate(internalInstance) {
  ReactUpdates.enqueueUpdate(internalInstance);
}
複製程式碼

  • 這個方法很關鍵
  • 這個方法的主要內容就是當 isBatchingUpdates = false 時,開啟批量更新模式
  • 否則就會將元件物件加入到批量更新元件的陣列中
    • 需要注意的是 isBatchingUpdates == true 就直接將元件push陣列.記住這個判斷
function enqueueUpdate(component) {
    //如果不處於批量更新模式
    if (!batchingStrategy.isBatchingUpdates) {
        batchingStrategy.batchedUpdates(enqueueUpdate, component);
        return;
    }
    //如果處於批量更新模式,則將該元件儲存在dirtyComponents陣列中
    dirtyComponents.push(component);
}
複製程式碼

  • 當沒有開啟批量更新模式的時候呼叫 batchingStrategy.batchedUpdates(enqueueUpdate, component);
  • batchedUpdates() 其實是ReactDefaultBatchingStrategy物件的一個函式
  • 不管開沒開起批量更新模式,都會先儲存之前的狀態,然後開啟更新模式
  • 如果沒開啟更新模式,就使用事物的方式呼叫回撥函式
/**
 * 呼叫batchingStrategy.batchedUpdates(enqueueUpdate, component);
 * ReactDefaultBatchingStrategy實際上是一個批量更新策略
 */
var ReactDefaultBatchingStrategy = {
    isBatchingUpdates: false,
    batchedUpdates: function(callback, a, b, c, d, e) {
		//第一次的時候為false
        var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
		//然後改成true,體會下什麼時候在改成true
        ReactDefaultBatchingStrategy.isBatchingUpdates = true;

        if (alreadyBatchingUpdates) {
            return callback(a, b, c, d, e);
        } else {
            return transaction.perform(callback, null, a, b, c, d, e);
        }
    },
};
複製程式碼
  • transaction.perform()
  • 這裡涉及到了事物的概念
  • Transaction會給每一個需要執行的方法包裝一個wrapper,而這個wrapper內有兩個方法 initialize 與 close 當要執行目標方法前要先執行 initialize() 然後才是目標方法 之後再執行close 而這裡 initialize() 是空函式
  • 簡而言之就是wrapper(initialize,perform,close)順序執行,而initialize還是個空函式,所以執行完perform,再執行close

  • RESET_BATCHED_UPDATES,FLUSH_BATCHED_UPDATES 定義了兩個wrapper
  • RESET_BATCHED_UPDATES 負責在close階段重置 ReactDefaultBatchingStrategy.isBatchingUpdates = false;
  • FLUSH_BATCHED_UPDATES 負責在close階段 執行ReactUpdates.flushBatchedUpdates.bind(ReactUpdates);
  • 然後把這兩個放到一個陣列中,在ReactDefaultBatchingStrategyTransaction的原型上繫結getTransactionWrappers用於返回wrapper的陣列;
var RESET_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: function() {
    ReactDefaultBatchingStrategy.isBatchingUpdates = false;
  },
};
var  FLUSH_BATCHED_UPDATES= {
  initialize: emptyFunction,
  close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates),
};

var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];

function ReactDefaultBatchingStrategyTransaction() {
  this.reinitializeTransaction();
}
/**
 *  把wrapper繫結到ReactDefaultBatchingStrategyTransaction的原型上
 */
Object.assign(ReactDefaultBatchingStrategyTransaction.prototype, Transaction, {
  getTransactionWrappers: function() {
    return TRANSACTION_WRAPPERS;
  },
});
複製程式碼

  • FLUSH_BATCHED_UPDATES.close=>ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)
  • 這個方法再迭代更新元件陣列,然後又以事物的方式呼叫了runBatchedUpdates()方法
  • 執行完回來釋放例項
var flushBatchedUpdates = function() {
    while (dirtyComponents.length || asapEnqueued) {
        if (dirtyComponents.length) {
            var transaction = ReactUpdatesFlushTransaction.getPooled();
            //又以事物的形式呼叫了 runBatchedUpdates()
            transaction.perform(runBatchedUpdates, null, transaction);
            //釋放例項
            ReactUpdatesFlushTransaction.release(transaction);
        }

        if (asapEnqueued) {
            asapEnqueued = false;
            var queue = asapCallbackQueue;
            asapCallbackQueue = CallbackQueue.getPooled();
            queue.notifyAll();
            CallbackQueue.release(queue);
        }
    }
};
複製程式碼

  • 根據名稱就知道這是個執行批量更新的操作
  • 這個方法主要就是呼叫了元件的performUpdateIfNecessary()
function runBatchedUpdates(transaction) {
  var len = transaction.dirtyComponentsLength;
  dirtyComponents.sort(mountOrderComparator);//排序,保證更新的順序
  updateBatchNumber++;
  for (var i = 0; i < len; i++) {
    var component = dirtyComponents[i];
    //在元件中獲取回撥函式
    var callbacks = component._pendingCallbacks;
    component._pendingCallbacks = null;

    var markerName;
    if (ReactFeatureFlags.logTopLevelRenders) {
      var namedComponent = component;
      if (component._currentElement.type.isReactTopLevelWrapper) {
        namedComponent = component._renderedComponent;
      }
      markerName = 'React update: ' + namedComponent.getName();
      console.time(markerName);
    }
    //經過一些列處理 其實就是呼叫了component.performUpdateIfNecessary,也就是自定義元件ReactCompositeComponent.performUpdateIfNecessary()
    ReactReconciler.performUpdateIfNecessary( component, transaction.reconcileTransaction, updateBatchNumber, );

    if (markerName) {
      console.timeEnd(markerName);
    }

    if (callbacks) {
      for (var j = 0; j < callbacks.length; j++) {
        transaction.callbackQueue.enqueue( callbacks[j], component.getPublicInstance());
      }
    }
  }
}
複製程式碼

  • 發現其實是呼叫了元件例項的performUpdateIfNecessary()

/**
 * 然後關鍵 ReactReconciler.performUpdateIfNecessary( component, transaction.reconcileTransaction, updateBatchNumber, );
 */
  performUpdateIfNecessary: function(internalInstance,transaction,updateBatchNumber,) {
    if (internalInstance._updateBatchNumber !== updateBatchNumber) {
      return;
    }
    internalInstance.performUpdateIfNecessary(transaction);
  },
複製程式碼

  • 然後又回到了ReactCompositeComponent自定義元件
  /**
   * internalInstance.performUpdateIfNecessary(transaction);
   *  呼叫了例項的performUpdateIfNecessary方法,這個例項就是自定義元件的例項
   */
    // 如果存在 _pendingElement、_pendingStateQueue和_pendingForceUpdate,則更新元件
  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;
    }
  },
複製程式碼

  • 跳到了非常熟悉的updateComponent方法
updateComponent: function(transaction,prevParentElement,nextParentElement,prevUnmaskedContext,nextUnmaskedContext,) {
  var inst = this._instance;
  var willReceive = false;
  var nextContext;

  // 上下文是否改變
  if (this._context === nextUnmaskedContext) {
    nextContext = inst.context;
  } else {
    nextContext = this._processContext(nextUnmaskedContext);
    willReceive = true;
  }

  var prevProps = prevParentElement.props;
  var nextProps = nextParentElement.props;

  // 新舊屬性不同
  if (prevParentElement !== nextParentElement) {
    willReceive = true;
  }
  //新舊屬性不同,並且存在componentWillReceiveProps,就執行componentWillReceiveProps()
  if (willReceive && inst.componentWillReceiveProps) {
    inst.componentWillReceiveProps(nextProps, nextContext);
  }
  //將新的state合併到更新佇列中,此時的nextState是最新的state
  var nextState = this._processPendingState(nextProps, nextContext);
  var shouldUpdate = true;
  //根據更新佇列和shouldComponentUpdate的狀態來判斷是否需要更新元件
  if (!this._pendingForceUpdate) {
    if (inst.shouldComponentUpdate) {
      shouldUpdate = inst.shouldComponentUpdate(nextProps,nextState,nextContext,);
    } else {
      if (this._compositeType === CompositeTypes.PureClass) {
        shouldUpdate =!shallowEqual(prevProps, nextProps) ||!shallowEqual(inst.state, nextState);
      }
    }
  }

  this._updateBatchNumber = null;
  if (shouldUpdate) {
    //重置更新佇列
    this._pendingForceUpdate = false;
    //即將更新this.props,this.state,和this.context
    this._performComponentUpdate(nextParentElement,nextProps, nextState,nextContext,transaction,nextUnmaskedContext,);
  } else {
    // 如果確定元件不更新,仍然要這是props和state
    this._currentElement = nextParentElement;
    this._context = nextUnmaskedContext;
    inst.props = nextProps;
    inst.state = nextState;
    inst.context = nextContext;
  }
},
/**
 * 呼叫了this._processPendingState(nextProps, nextContext)
 */
_processPendingState: function(props, context) {
  var inst = this._instance;
  var queue = this._pendingStateQueue;
  var replace = this._pendingReplaceState;
  this._pendingReplaceState = false;
  this._pendingStateQueue = null;
  //如果佇列為null,返回原state
  if (!queue) {
    return inst.state;
  }
  //如果佇列中有一個更新就返回這個更新值
  if (replace && queue.length === 1) {
    return queue[0];
  }
  //如果佇列中有多個更新,就將他們合併
  var nextState = Object.assign({}, replace ? queue[0] : inst.state);
  for (var i = replace ? 1 : 0; i < queue.length; i++) {
    var partial = queue[i];
    //這裡就是setState的第一個引數的另外一種情況,可以傳入函式
    //這裡用函式的形式,合併到了nextState上,就獲取了最新的state值
    Object.assign(
      nextState,
      typeof partial === 'function'
        ? partial.call(inst, nextState, props, context)
        : partial,
    );
  }
  //返回了最新的state的值
  return nextState;
},
複製程式碼

  • 在執行完componentWillUpdate 之後才能獲取最新的state值
 /**
   * 當元件需要更新時,呼叫
   */
  _performComponentUpdate: function(nextElement,nextProps,nextState,nextContext,transaction,unmaskedContext) {
    var inst = this._instance;
    var hasComponentDidUpdate = Boolean(inst.componentDidUpdate);
    var prevProps;
    var prevState;
    var prevContext;
    //如果存在componentDidUpdate,則將當前的props,state和context儲存一份
    if (hasComponentDidUpdate) {
      prevProps = inst.props;
      prevState = inst.state;
      prevContext = inst.context;
    }
    //執行componentWillUpdate
    if (inst.componentWillUpdate) {
      inst.componentWillUpdate(nextProps, nextState, nextContext);
    }
    //更新this.props,this.state,this.context
	//這個時候才開始給例項的props和state,context賦值
    this._currentElement = nextElement;
    this._context = unmaskedContext;
    inst.props = nextProps;
    inst.state = nextState;
    inst.context = nextContext;
    //渲染元件
    this._updateRenderedComponent(transaction, unmaskedContext);
    //當元件完成更新後,如果存在ComponentDidUpdate,則呼叫
    if (hasComponentDidUpdate) {
      transaction.getReactMountReady().enqueue(inst.componentDidUpdate.bind(inst,prevProps, prevState,prevContext,),inst,);
    }
  },
/**
  *	這裡runBatchedUpdates的事物才執行完畢,也就是說在componentDidUpdate()之後呼叫事物的close,
  */
複製程式碼

setState的第二個引數問題

ReactComponent.prototype.setState = function(partialState, callback) {
	//這裡的this.updater就是ReactUpdateQueue,--- this是元件的例項
  this.updater.enqueueSetState(this, partialState);
  if (callback) {
    this.updater.enqueueCallback(this, callback, 'setState');
  }
};
/**
 * 跟enqueueSetState 類似
 * 執行這個方法最主要的就是將回撥函式繫結到了internalInstance._pendingCallbacks上
 */
enqueueCallback: function(publicInstance, callback, callerName) {
    var internalInstance = getInternalInstanceReadyForUpdate(publicInstance);
    if (!internalInstance) {
      return null;
    }
    //就是初始化_pendingCallbacks
    if (internalInstance._pendingCallbacks) {
      internalInstance._pendingCallbacks.push(callback);
    } else {
      internalInstance._pendingCallbacks = [callback];
    }
    
    enqueueUpdate(internalInstance);
},
/**
 * 最後又跳到了這裡,就不重要了
 */
function enqueueUpdate(internalInstance) {
  ReactUpdates.enqueueUpdate(internalInstance);
}
/**
 * 回撥函式是在setState執行完之後再執行的
 */

/**
 *  該方法用於迭代dirtyComponents
 */
var flushBatchedUpdates = function() {
    while (dirtyComponents.length || asapEnqueued) {
        if (dirtyComponents.length) {
            var transaction = ReactUpdatesFlushTransaction.getPooled();
            //又以事物的形式呼叫了 runBatchedUpdates()
            transaction.perform(runBatchedUpdates, null, transaction);
            //釋放例項
            ReactUpdatesFlushTransaction.release(transaction);
        }

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

/**
 * 以下是ReactUpdatesFlushTransaction定義
 * 在close的時候發現呼叫了this.callbackQueue.notifyAll();
 * 而notifyAll方法是取出回撥函式開始執行
 */
var UPDATE_QUEUEING = {
  initialize: function() {
    this.callbackQueue.reset();
  },
  close: function() {
    this.callbackQueue.notifyAll();
  },
};

var TRANSACTION_WRAPPERS = [NESTED_UPDATES, UPDATE_QUEUEING];

function ReactUpdatesFlushTransaction() {
  this.reinitializeTransaction();
  this.dirtyComponentsLength = null;
  this.callbackQueue = CallbackQueue.getPooled();
  this.reconcileTransaction = ReactUpdates.ReactReconcileTransaction.getPooled(true,);
}

  /**
   * 執行回撥函式
   */
  notifyAll() {
    var callbacks = this._callbacks;
    var contexts = this._contexts;
    var arg = this._arg;
    if (callbacks && contexts) {
      this._callbacks = null;
      this._contexts = null;
      for (var i = 0; i < callbacks.length; i++) {
        callbacks[i].call(contexts[i], arg);
      }
      callbacks.length = 0;
      contexts.length = 0;
    }
  }
複製程式碼

setState的非同步處理


這個可以追蹤原始碼理解,經過上面的流程那麼這個就不難解釋了

  1. 直接呼叫this.setState()
  • 因為在整個生命週期中就是一個事物操作,所以標識位isBatchingUpdates = true,所以流程到了enqueueUpdate()時,例項物件都會加入到dirtyComponents 陣列中
  • 接著執行componentWillMount()
  • 將所有的setState操作進行合併(相同的操作合併成一個)
  • 渲染
  • 執行componentDidMount()
  • 事物結束,呼叫close方法->呼叫runBatchUpdate()
  • 繼續走更新流程
  1. 使用setTimeout()方法呼叫
  • setTimeout()沒在事物中 isBatchingUpdates = false 所以就直接走直接更新操作

相關文章