首先舉一個最簡單的例子:react
this.state={ a:1 } this.setState({ a:2 }) console.log(this.state.a)
//1
能夠說setState()操做是一個異步,由於要將一段時間內的state改變壓入棧,並最終執行一次,同時也是優化性能的一部份數組
可是:app
定時器:異步
定時器中的setState,每次都會引發新的render,即便是同一個定時器中的屢次setState,函數
由於定時器中的回調不屬於react控制性能
原生事件:優化
下面簡單講解一下源碼的理解this
入口:spa
ReactComponent.prototype.setState = function (partialState, callback) { // 將setState事務放入隊列中 this.updater.enqueueSetState(this, partialState); if (callback) { this.updater.enqueueCallback(this, callback, 'setState'); } };
partialState:部分state,傳入enqueueSetState(),若是有回調函數,則調用enqueueCallback(),在更改後執行prototype
enquenueSetState:
enqueueSetState: function (publicInstance, partialState) { // 先獲取ReactComponent組件對象 var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState'); if (!internalInstance) { return; } // 若是_pendingStateQueue爲空,則建立它。能夠發現隊列是數組形式實現的 var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []); queue.push(partialState); // 將要更新的ReactComponent放入數組中 enqueueUpdate(internalInstance); }
經過getInternalInstanceReadyForUpdate()獲取到ReactComponent組件對象,
判斷這個組件的state等待隊列是否爲空,爲空則建立爲空數組,並將state push進這個數組,
將要更新的組件傳入enqueueUpdate()並執行
getInternalInstanceReadyForUpdate
function getInternalInstanceReadyForUpdate(publicInstance, callerName) { // 從map取出ReactComponent組件,還記得mountComponent時把ReactElement做爲key,將ReactComponent存入了map中了吧,ReactComponent是React組件的核心,包含各類狀態,數據和操做方法。而ReactElement則僅僅是一個數據類。 var internalInstance = ReactInstanceMap.get(publicInstance); if (!internalInstance) { return null; } return internalInstance; }
publicInstance:enqueueSetState方法傳入的this,指當前組件實例
從map取出ReactComponent組件,還記得mountComponent時把ReactElement做爲key,
將ReactComponent存入了map中了吧,ReactComponent是React組件的核心,包含各類狀態,數據和操做方法。而ReactElement則僅僅是一個數據類
注:(virtual DOM 就是由一個個的React element 組成。React element, 是由React 庫提供的createElement 函數建立而來)
enqueueUpdate:
function enqueueUpdate(component) { ensureInjected(); // 若是不是正處於建立或更新組件階段,則處理update事務 if (!batchingStrategy.isBatchingUpdates) { batchingStrategy.batchedUpdates(enqueueUpdate, component); return; } // 若是正在建立或更新組件,則暫且先不處理update,只是將組件放在dirtyComponents數組中 dirtyComponents.push(component); }
基本邏輯:若是如今是正在建立或者更新組件的階段,則把組件放入dirtyComponents數組中,並不先update,不然就進行batchedUpdates()
enqueueUpdate包含了React避免重複render的邏輯。mountComponent和updateComponent方法在執行的最開始,會調用到batchedUpdates進行批處理更新,此時會將isBatchingUpdates設置爲true,也就是將狀態標記爲如今正處於更新階段了
。(——能夠不看
以後React以事務的方式處理組件update,事務處理完後會調用wrapper.close(), 而TRANSACTION_WRAPPERS中包含了RESET_BATCHED_UPDATES這個wrapper,故最終會調用RESET_BATCHED_UPDATES.close(),
——)
它最終會將isBatchingUpdates設置爲false。這個過程比較麻煩,想更清晰的瞭解的話,建議自行分析源碼。
故getInitialState,componentWillMount, render,componentWillUpdate 中setState都不會引發updateComponent。但在componentDidMount和componentDidUpdate中則會。
batchedUpdates:
batchedUpdates: function (callback, a, b, c, d, e) { var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates; // 批處理最開始時,將isBatchingUpdates設爲true,代表正在更新 ReactDefaultBatchingStrategy.isBatchingUpdates = true; // The code is written this way to avoid extra allocations if (alreadyBatchingUpdates) { callback(a, b, c, d, e); } else { // 以事務的方式處理updates,後面詳細分析transaction transaction.perform(callback, null, a, b, c, d, e); } } var RESET_BATCHED_UPDATES = { initialize: emptyFunction, close: function () { // 事務批更新處理結束時,將isBatchingUpdates設爲了false ReactDefaultBatchingStrategy.isBatchingUpdates = false; } }; var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];
設置兩個wrapper,
RESET_BATCHED_UPDATES設置isBatchingUpdates,
FLUSH_BATCHED_UPDATES會在一個transaction的close階段運行runBatchedUpdates,從而執行update
不詳述,百度不少文章,大致思路:
runBatchedUpdates:
function runBatchedUpdates(transaction) { var len = transaction.dirtyComponentsLength; dirtyComponents.sort(mountOrderComparator); for (var i = 0; i < len; i++) { // dirtyComponents中取出一個component var component = dirtyComponents[i]; // 取出dirtyComponent中的未執行的callback,下面就準備執行它了 var callbacks = component._pendingCallbacks; component._pendingCallbacks = null; var markerName; if (ReactFeatureFlags.logTopLevelRenders) { var namedComponent = component; if (component._currentElement.props === component._renderedComponent._currentElement) { namedComponent = component._renderedComponent; } } // 執行updateComponent ReactReconciler.performUpdateIfNecessary(component, transaction.reconcileTransaction); // 執行dirtyComponent中以前未執行的callback if (callbacks) { for (var j = 0; j < callbacks.length; j++) { transaction.callbackQueue.enqueue(callbacks[j], component.getPublicInstance()); } } } }
runBatchedUpdates循環遍歷dirtyComponents數組,主要幹兩件事。
首先執行performUpdateIfNecessary來刷新組件的view,
而後執行以前阻塞的callback。
下面來看performUpdateIfNecessary。
performUpdateIfNecessary: function (transaction) { if (this._pendingElement != null) { // receiveComponent會最終調用到updateComponent,從而刷新View ReactReconciler.receiveComponent(this, this._pendingElement, transaction, this._context); } if (this._pendingStateQueue !== null || this._pendingForceUpdate) { // 執行updateComponent,從而刷新View。這個流程在React生命週期中講解過 this.updateComponent(transaction, this._currentElement, this._currentElement, this._context, this._context); } },
執行updateComponent進行狀態更新,值得注意的是更新操做內會調用
_processPendingState
進行原有
state
的合併以及設置
this._pendingStateQueue = null
,這也就意味着
dirtyComponents
進入下一次循環時,執行
performUpdateIfNecessary
不會再去更新組件
總結:
setState流程仍是很複雜的,設計也很精巧,避免了重複無謂的刷新組件。它的主要流程以下
推薦完整地址:https://yq.aliyun.com/articles/72329?t=t1