這篇文章將為大家詳細(xì)講解有關(guān)React中setState源碼的示例分析,小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
十余年的賈汪網(wǎng)站建設(shè)經(jīng)驗(yàn),針對(duì)設(shè)計(jì)、前端、開(kāi)發(fā)、售后、文案、推廣等六對(duì)一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。網(wǎng)絡(luò)營(yíng)銷推廣的優(yōu)勢(shì)是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整賈汪建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無(wú)論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)從事“賈汪網(wǎng)站設(shè)計(jì)”,“賈汪網(wǎng)站推廣”以來(lái),每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。
React作為一門(mén)前端框架,雖然只是focus在MVVM中的View部分,但還是實(shí)現(xiàn)了View和model的綁定。修改數(shù)據(jù)的同時(shí),可以實(shí)現(xiàn)View的刷新。這大大簡(jiǎn)化了我們的邏輯,只用關(guān)心數(shù)據(jù)流的變化,同時(shí)減少了代碼量,使得后期維護(hù)也更加方便。這個(gè)特性則要?dú)w功于setState()方法。React中利用隊(duì)列機(jī)制來(lái)管理state,避免了很多重復(fù)的View刷新。下面我們來(lái)從源碼角度探尋下setState機(jī)制。
1 還是先聲明一個(gè)組件,從最開(kāi)始一步步來(lái)尋源;
class App extends Component { //只在組件重新加載的時(shí)候執(zhí)行一次 constructor(props) { super(props); //.. } //other methods } //ReactBaseClasses.js中如下:這里就是setState函數(shù)的來(lái)源; //super其實(shí)就是下面這個(gè)函數(shù) function ReactComponent(props, context, updater) { this.props = props; this.context = context; this.refs = emptyObject; // We initialize the default updater but the real one gets injected by the // renderer. this.updater = updater || ReactNoopUpdateQueue; } ReactComponent.prototype.setState = function (partialState, callback) { this.updater.enqueueSetState(this, partialState); if (callback) { this.updater.enqueueCallback(this, callback, 'setState'); } };
所以主要來(lái)看是否傳入了updater參數(shù),也就是說(shuō)何時(shí)進(jìn)行 new 組件;具體的updater參數(shù)是怎么傳遞進(jìn)來(lái)的,以及是那個(gè)對(duì)象,參見(jiàn)
react源碼分析系列文章下面的react中context updater到底是如何傳遞的
這里直接說(shuō)結(jié)果,updater對(duì)象其實(shí)就是ReactUpdateQueue.js 中暴漏出的ReactUpdateQueue對(duì)象;
2 既然找到了setState之后執(zhí)行的動(dòng)作,我們?cè)谝徊讲缴钊脒M(jìn)去
class Root extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } componentDidMount() { let me = this; me.setState({ count: me.state.count + 1 }); console.log(me.state.count); // 打印出0 me.setState({ count: me.state.count + 1 }); console.log(me.state.count); // 打印出0 setTimeout(function(){ me.setState({ count: me.state.count + 1 }); console.log(me.state.count); // 打印出2 }, 0); setTimeout(function(){ me.setState({ count: me.state.count + 1 }); console.log(me.state.count); // 打印出3 }, 0); } render() { return ({this.state.count}
) } } ReactComponent.prototype.setState = function (partialState, callback) { this.updater.enqueueSetState(this, partialState); if (callback) { this.updater.enqueueCallback(this, callback, 'setState'); } };
ReactUpdateQueue.js
var ReactUpdates = require('./ReactUpdates'); function enqueueUpdate(internalInstance) { ReactUpdates.enqueueUpdate(internalInstance); }; function getInternalInstanceReadyForUpdate(publicInstance, callerName) { //在ReactCompositeComponent.js中有這樣一行代碼,這就是其來(lái)源; // Store a reference from the instance back to the internal representation //ReactInstanceMap.set(inst, this); var internalInstance = ReactInstanceMap.get(publicInstance); //返回的是在ReactCompositeComponent.js中construct函數(shù)返回的對(duì)象;ReactInstance實(shí)例對(duì)象并不是簡(jiǎn)單的new 我們寫(xiě)的組件的實(shí)例對(duì)象,而是經(jīng)過(guò)instantiateReactComponent.js中ReactCompositeComponentWrapper函數(shù)包裝的對(duì)象;詳見(jiàn) 創(chuàng)建React組件方式以及源碼分析.md return internalInstance; }; var ReactUpdateQueue = { //。。。。省略其他代碼 enqueueCallback: function (publicInstance, callback, callerName) { ReactUpdateQueue.validateCallback(callback, callerName); var internalInstance = getInternalInstanceReadyForUpdate(publicInstance); if (!internalInstance) { return null; } //這里將callback放入組件實(shí)例的_pendingCallbacks數(shù)組中; if (internalInstance._pendingCallbacks) { internalInstance._pendingCallbacks.push(callback); } else { internalInstance._pendingCallbacks = [callback]; } // TODO: The callback here is ignored when setState is called from // componentWillMount. Either fix it or disallow doing so completely in // favor of getInitialState. Alternatively, we can disallow // componentWillMount during server-side rendering. enqueueUpdate(internalInstance); }, enqueueSetState: function (publicInstance, partialState) { var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState'); if (!internalInstance) { return; } //這里,初始化queue變量,同時(shí)初始化 internalInstance._pendingStateQueue = [ ] ; //對(duì)于 || 的短路運(yùn)算還是要多梳理下 //queue數(shù)組(模擬隊(duì)列)中存放著setState放進(jìn)來(lái)的對(duì)象; var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []); //這里將partialState放入queue數(shù)組中,也就是internalInstance._pendingStateQueue 數(shù)組中,此時(shí),每次setState的partialState,都放進(jìn)了React組件實(shí)例對(duì)象上的_pendingStateQueue屬性中,成為一個(gè)數(shù)組; queue.push(partialState); enqueueUpdate(internalInstance); }, }; module.exports = ReactUpdateQueue;
可以看到enqueueSetState enqueueCallback 最后都會(huì)執(zhí)行enqueueUpdate;
function enqueueUpdate(internalInstance) { ReactUpdates.enqueueUpdate(internalInstance); }
ReactUpdates.js
var dirtyComponents = []; var updateBatchNumber = 0; var asapCallbackQueue = CallbackQueue.getPooled(); var asapEnqueued = false; //這里聲明batchingStrategy為null,后期通過(guò)注冊(cè)給其賦值; var batchingStrategy = null; //這里的component參數(shù)是js中ReactCompositeComponentWrapper函數(shù)包裝的后的React組件實(shí)例對(duì)象; function enqueueUpdate(component) { ensureInjected(); //第一次執(zhí)行setState的時(shí)候,可以進(jìn)入if語(yǔ)句,遇到里面的return語(yǔ)句,終止執(zhí)行 //如果不是正處于創(chuàng)建或更新組件階段,則處理update事務(wù) if (!batchingStrategy.isBatchingUpdates) { //batchedUpdates就是ReactDefaultBatchingStrategy.js中聲明的 batchingStrategy.batchedUpdates(enqueueUpdate, component); return; } //第二次執(zhí)行setState的時(shí)候,進(jìn)入不了if語(yǔ)句,將組件放入dirtyComponents //如果正在創(chuàng)建或更新組件,則暫且先不處理update,只是將組件放在dirtyComponents數(shù)組中 dirtyComponents.push(component); if (component._updateBatchNumber == null) { component._updateBatchNumber = updateBatchNumber + 1; } }; //enqueueUpdate包含了React避免重復(fù)render的邏輯。mountComponent和updateComponent方法在執(zhí)行的最開(kāi)始,會(huì)調(diào)用到batchedUpdates進(jìn)行批處理更新,此時(shí)會(huì)將isBatchingUpdates設(shè)置為true,也就是將狀態(tài)標(biāo)記為現(xiàn)在正處于更新階段了。之后React以事務(wù)的方式處理組件update,事務(wù)處理完后會(huì)調(diào)用wrapper.close(), 而TRANSACTION_WRAPPERS中包含了RESET_BATCHED_UPDATES這個(gè)wrapper,故最終會(huì)調(diào)用RESET_BATCHED_UPDATES.close(), 它最終會(huì)將isBatchingUpdates設(shè)置為false。
ReactDefaultBatchingStrategy.js
//RESET_BATCHED_UPDATES用來(lái)管理isBatchingUpdates的狀態(tài) var RESET_BATCHED_UPDATES = { initialize: emptyFunction, close: function () { // 事務(wù)批更新處理結(jié)束時(shí),將isBatchingUpdates設(shè)為了false ReactDefaultBatchingStrategy.isBatchingUpdates = false; } }; //FLUSH_BATCHED_UPDATES會(huì)在一個(gè)transaction的close階段運(yùn)行runBatchedUpdates,從而執(zhí)行update。 //因?yàn)閏lose的執(zhí)行順序是FLUSH_BATCHED_UPDATES.close ==> 然后RESET_BATCHED_UPDATES.close var FLUSH_BATCHED_UPDATES = { initialize: emptyFunction, close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates) }; var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES]; function ReactDefaultBatchingStrategyTransaction() { this.reinitializeTransaction(); } _assign(ReactDefaultBatchingStrategyTransaction.prototype, Transaction, { getTransactionWrappers: function () { return TRANSACTION_WRAPPERS; } }); //這個(gè)transition就是下面ReactDefaultBatchingStrategy對(duì)象中使用的transaction變量 var transaction = new ReactDefaultBatchingStrategyTransaction(); var ReactDefaultBatchingStrategy = { isBatchingUpdates: false, /** * Call the provided function in a context within which calls to `setState` * and friends are batched such that components aren't updated unnecessarily. */ batchedUpdates: function (callback, a, b, c, d, e) { var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates; // 批處理最開(kāi)始時(shí),將isBatchingUpdates設(shè)為true,表明正在更新 ReactDefaultBatchingStrategy.isBatchingUpdates = true; // The code is written this way to avoid extra allocations if (alreadyBatchingUpdates) { return callback(a, b, c, d, e); } else { //transition在上面已經(jīng)聲明; // 以事務(wù)的方式處理updates,后面詳細(xì)分析transaction return transaction.perform(callback, null, a, b, c, d, e); } } }; module.exports = ReactDefaultBatchingStrategy;
接下來(lái)我們看下React中的事物處理機(jī)制到底是如何運(yùn)行的;
Transaction.js
var _prodInvariant = require('./reactProdInvariant'); var invariant = require('fbjs/lib/invariant'); var OBSERVED_ERROR = {}; var TransactionImpl = { reinitializeTransaction: function () { //getTransactionWrappers這個(gè)函數(shù)ReactDefaultBatchingStrategy.js中聲明的,上面有;返回一個(gè)數(shù)組; this.transactionWrappers = this.getTransactionWrappers(); if (this.wrapperInitData) { this.wrapperInitData.length = 0; } else { this.wrapperInitData = []; } this._isInTransaction = false; }, _isInTransaction: false, getTransactionWrappers: null, isInTransaction: function () { return !!this._isInTransaction; }, perform: function (method, scope, a, b, c, d, e, f) { var errorThrown; var ret; try { this._isInTransaction = true; errorThrown = true; //var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES]; //1 這里會(huì)先執(zhí)行所有的TRANSACTION_WRAPPERS中成員的initialize方法,上面聲明的其都是emptyFunction this.initializeAll(0); //2 這里其實(shí)還是執(zhí)行的 enqueueUpdate 函數(shù) ret = method.call(scope, a, b, c, d, e, f); errorThrown = false; } finally { try { if (errorThrown) { // If `method` throws, prefer to show that stack trace over any thrown // by invoking `closeAll`. try { this.closeAll(0); } catch (err) {} } else { // Since `method` didn't throw, we don't want to silence the exception // here. //3 執(zhí)行TRANSACTION_WRAPPERS對(duì)象中成員的所有close方法; this.closeAll(0); } } finally { this._isInTransaction = false; } } return ret; }, initializeAll: function (startIndex) { var transactionWrappers = this.transactionWrappers; for (var i = startIndex; i < transactionWrappers.length; i++) { var wrapper = transactionWrappers[i]; try { this.wrapperInitData[i] = OBSERVED_ERROR; this.wrapperInitData[i] = wrapper.initialize ? wrapper.initialize.call(this) : null; } finally { if (this.wrapperInitData[i] === OBSERVED_ERROR) { try { this.initializeAll(i + 1); } catch (err) {} } } } }, closeAll: function (startIndex) { var transactionWrappers = this.transactionWrappers; for (var i = startIndex; i < transactionWrappers.length; i++) { var wrapper = transactionWrappers[i]; var initData = this.wrapperInitData[i]; var errorThrown; try { errorThrown = true; if (initData !== OBSERVED_ERROR && wrapper.close) { wrapper.close.call(this, initData); } errorThrown = false; } finally { if (errorThrown) { try { this.closeAll(i + 1); } catch (e) {} } } } this.wrapperInitData.length = 0; } }; module.exports = TransactionImpl //3 執(zhí)行TRANSACTION_WRAPPERS對(duì)象中成員的所有close方法; var FLUSH_BATCHED_UPDATES = { initialize: emptyFunction, close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates) };
接著會(huì)執(zhí)行ReactUpdates.js中的flushBatchedUpdates方法
ReactUpdates.js中
var flushBatchedUpdates = function () { while (dirtyComponents.length || asapEnqueued) { if (dirtyComponents.length) { var transaction = ReactUpdatesFlushTransaction.getPooled(); //這里執(zhí)行runBatchedUpdates函數(shù); transaction.perform(runBatchedUpdates, null, transaction); ReactUpdatesFlushTransaction.release(transaction); } if (asapEnqueued) { asapEnqueued = false; var queue = asapCallbackQueue; asapCallbackQueue = CallbackQueue.getPooled(); queue.notifyAll(); CallbackQueue.release(queue); } } }; 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; // Duck type TopLevelWrapper. This is probably always true. if (component._currentElement.type.isReactTopLevelWrapper) { namedComponent = component._renderedComponent; } markerName = 'React update: ' + namedComponent.getName(); console.time(markerName); } //這里才是真正的開(kāi)始更新組件 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()); } } } }
ReactReconciler.js中
performUpdateIfNecessary: function (internalInstance, transaction, updateBatchNumber) { if (internalInstance._updateBatchNumber !== updateBatchNumber) { // The component's enqueued batch number should always be the current // batch or the following one. return; } //這里執(zhí)行React組件實(shí)例對(duì)象的更新;internalInstance上的performUpdateIfNecessary在ReactCompositeComponent.js中的; internalInstance.performUpdateIfNecessary(transaction); if (process.env.NODE_ENV !== 'production') { if (internalInstance._debugID !== 0) { ReactInstrumentation.debugTool.onUpdateComponent(internalInstance._debugID); } } }
ReactCompositeComponent.js
performUpdateIfNecessary: function (transaction) { if (this._pendingElement != null) { // receiveComponent會(huì)最終調(diào)用到updateComponent,從而刷新View ReactReconciler.receiveComponent(this, this._pendingElement, transaction, this._context); } else if (this._pendingStateQueue !== null || this._pendingForceUpdate) { // 執(zhí)行updateComponent,從而刷新View。 this.updateComponent(transaction, this._currentElement, this._currentElement, this._context, this._context); } else { this._updateBatchNumber = null; } }, //執(zhí)行更新React組件的props. state。context函數(shù) updateComponent: function (transaction, prevParentElement, nextParentElement, prevUnmaskedContext, nextUnmaskedContext) { var inst = this._instance; var willReceive = false; var nextContext; // Determine if the context has changed or not if (this._context === nextUnmaskedContext) { nextContext = inst.context; } else { nextContext = this._processContext(nextUnmaskedContext); willReceive = true; } var prevProps = prevParentElement.props; var nextProps = nextParentElement.props; // Not a simple state update but a props update if (prevParentElement !== nextParentElement) { willReceive = true; } // An update here will schedule an update but immediately set // _pendingStateQueue which will ensure that any state updates gets // immediately reconciled instead of waiting for the next batch. if (willReceive && inst.componentWillReceiveProps) { if (process.env.NODE_ENV !== 'production') { measureLifeCyclePerf(function () { return inst.componentWillReceiveProps(nextProps, nextContext); }, this._debugID, 'componentWillReceiveProps'); } else { inst.componentWillReceiveProps(nextProps, nextContext); } } //這里可以知道為什么setState可以接受函數(shù),主要就是_processPendingState函數(shù); //這里僅僅是將每次setState放入到_pendingStateQueue隊(duì)列中的值,合并到nextState,并沒(méi)有真正的更新state的值;真正更新組件的state的值是在下面; var nextState = this._processPendingState(nextProps, nextContext); var shouldUpdate = true; if (!this._pendingForceUpdate) { if (inst.shouldComponentUpdate) { if (process.env.NODE_ENV !== 'production') { shouldUpdate = measureLifeCyclePerf(function () { return inst.shouldComponentUpdate(nextProps, nextState, nextContext); }, this._debugID, 'shouldComponentUpdate'); } else { 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; // Will set `this.props`, `this.state` and `this.context`. this._performComponentUpdate(nextParentElement, nextProps, nextState, nextContext, transaction, nextUnmaskedContext); } else { // If it's determined that a component should not update, we still want // to set props and state but we shortcut the rest of the update. //諾:在這里更新組件的state. props 等值; this._currentElement = nextParentElement; this._context = nextUnmaskedContext; inst.props = nextProps; inst.state = nextState; inst.context = nextContext; } }, _processPendingState: function (props, context) { var inst = this._instance; var queue = this._pendingStateQueue; var replace = this._pendingReplaceState; this._pendingReplaceState = false; this._pendingStateQueue = null; if (!queue) { return inst.state; } if (replace && queue.length === 1) { return queue[0]; } var nextState = _assign({}, replace ? queue[0] : inst.state); for (var i = replace ? 1 : 0; i < queue.length; i++) { var partial = queue[i]; //如果是setState的參數(shù)是一個(gè)函數(shù),那么該函數(shù)接受三個(gè)參數(shù),分別是state props context _assign(nextState, typeof partial === 'function' ? partial.call(inst, nextState, props, context) : partial); } return nextState; },
this.state的更新會(huì)在_processPendingState執(zhí)行完執(zhí)行。所以兩次setState取到的都是this.state.count最初的值0,這就解釋了之前的現(xiàn)象。其實(shí),這也是React為了解決這種前后state依賴但是state又沒(méi)及時(shí)更新的一種方案,因此在使用時(shí)大家要根據(jù)實(shí)際情況來(lái)判斷該用哪種方式傳參。來(lái)看個(gè)小例子直觀感受下
handleClickOnLikeButton () { this.setState({ count: 0 }) // => this.state.count 還是 undefined this.setState({ count: this.state.count + 1}) // => undefined + 1 = NaN this.setState({ count: this.state.count + 2}) // => NaN + 2 = NaN } //....VS .... handleClickOnLikeButton () { this.setState((prevState) => { return { count: 0 } }) this.setState((prevState) => { return { count: prevState.count + 1 } // 上一個(gè) setState 的返回是 count 為 0,當(dāng)前返回 1 }) this.setState((prevState) => { return { count: prevState.count + 2 } // 上一個(gè) setState 的返回是 count 為 1,當(dāng)前返回 3 }) // 最后的結(jié)果是 this.state.count 為 3 } ...
setState流程還是很復(fù)雜的,設(shè)計(jì)也很精巧,避免了重復(fù)無(wú)謂的刷新組件。它的主要流程如下
enqueueSetState將state放入隊(duì)列中,并調(diào)用enqueueUpdate處理要更新的Component
如果組件當(dāng)前正處于update事務(wù)中,則先將Component存入dirtyComponent中。否則調(diào)用batchedUpdates處理。
batchedUpdates發(fā)起一次transaction.perform()事務(wù)
開(kāi)始執(zhí)行事務(wù)初始化,運(yùn)行,結(jié)束三個(gè)階段
初始化:事務(wù)初始化階段沒(méi)有注冊(cè)方法,故無(wú)方法要執(zhí)行
運(yùn)行:執(zhí)行setSate時(shí)傳入的callback方法,一般不會(huì)傳callback參數(shù)
結(jié)束:更新isBatchingUpdates為false,并執(zhí)行FLUSH_BATCHED_UPDATES這個(gè)wrapper中的close方法
FLUSH_BATCHED_UPDATES在close階段,會(huì)循環(huán)遍歷所有的dirtyComponents,調(diào)用updateComponent刷新組件,并執(zhí)行它的pendingCallbacks, 也就是setState中設(shè)置的callback。
關(guān)于“React中setState源碼的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。