以程式碼註釋的方式整理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的非同步處理
這個可以追蹤原始碼理解,經過上面的流程那麼這個就不難解釋了
- 直接呼叫this.setState()
- 因為在整個生命週期中就是一個事物操作,所以標識位isBatchingUpdates = true,所以流程到了enqueueUpdate()時,例項物件都會加入到dirtyComponents 陣列中
- 接著執行componentWillMount()
- 將所有的setState操作進行合併(相同的操作合併成一個)
- 渲染
- 執行componentDidMount()
- 事物結束,呼叫close方法->呼叫runBatchUpdate()
- 繼續走更新流程
- 使用setTimeout()方法呼叫
- setTimeout()沒在事物中 isBatchingUpdates = false 所以就直接走直接更新操作