摘要:下面是的源碼如其名,是一個(gè)隊(duì)列操作,將要變更的統(tǒng)一插入隊(duì)列,待一一處理。只有控制下的事件周期,會(huì)執(zhí)行切換狀態(tài),保證批量操作能被截獲并插入堆棧。也就是源碼中的。他在初始被賦值為,也就是以下兩個(gè)對(duì)象,在源碼被稱作為。本文參考源碼版本為。
React setState
不知道什么時(shí)候開始,很多人開始認(rèn)為setState是異步操作,所謂的異步操作,就是我們?cè)趫?zhí)行了setState之后,立即通過(guò)this.state.xxx不能拿到更新之后的值。這樣的認(rèn)知其實(shí)有一種先入為主的意識(shí),也許是受到很多不知名博主的不科學(xué)言論導(dǎo)致的錯(cuò)誤認(rèn)知,也有可能是日常開發(fā)過(guò)程中積累的經(jīng)驗(yàn)。畢竟大部分開發(fā)寫setState這樣的方法,都是在組件的生命周期(如componentDidMount、componentWillMount)中,或者react的事件處理機(jī)制中,這種教科書式的寫代碼方式,基本不會(huì)碰到有數(shù)據(jù)異常。
雖然官方文檔對(duì)setState這種同步行為語(yǔ)焉不詳,但是我們可以發(fā)現(xiàn)某些情況下,setState是真的可以同步獲取數(shù)據(jù)的。通過(guò)本文我們可以了解react這方面的工作原理,對(duì)于我們的思考開發(fā)方案,解決疑難問(wèn)題,避免不必要的錯(cuò)誤,也許會(huì)有不少幫助。
我們先來(lái)說(shuō)結(jié)論:
在React中,如果是由React引發(fā)的事件處理(比如通過(guò)onClick引發(fā)的事件處理,componentWillMount等生命周期),調(diào)用setState不會(huì)同步更新this.state;除此之外的setState調(diào)用會(huì)同步執(zhí)行this.state。所謂“除此之外”,指的是繞過(guò)React通過(guò)addEventListener直接添加的事件處理函數(shù),還有通過(guò)setTimeout/setInterval產(chǎn)生的異步調(diào)用。
不想看長(zhǎng)篇大論的同學(xué),到這里就可以結(jié)束了。想了解原理的同學(xué)請(qǐng)繼續(xù)參觀。。
用過(guò)angular框架的同學(xué)也許記得angular的代碼模式中有一個(gè)$timeout這樣的調(diào)用方法,和setTimeout功能基本一致,但是setTimeout卻不能實(shí)時(shí)觸發(fā)UI的更新。這是因?yàn)?b>$timeout比setTimeout添加了對(duì)UI更新(臟檢查)的處理,在延時(shí)結(jié)束后立即調(diào)用更新方法更新UI的渲染。同樣的道理,我們必須使用react指定的方式更新state才能同步UI的渲染,因?yàn)閞eact控制下的事件會(huì)同步處理UI的更新。而直接使用this.state.xxx = xxx這樣的方式僅僅改變了數(shù)據(jù),沒(méi)有改變UI,這就不是React倡導(dǎo)的reactive programing了。
實(shí)際上,在react的源碼中我們會(huì)發(fā)現(xiàn),大部分react控制下的事件或生命周期,會(huì)調(diào)用batchedUpdates(查看如下代碼)。這個(gè)方法會(huì)觸發(fā)component渲染的狀態(tài)isBatchingUpdates。同樣的,react的事件監(jiān)聽(tīng)機(jī)制會(huì)觸發(fā)batchedUpdates方法,同樣會(huì)將isBatchingUpdates狀態(tài)置為true。
// 更新?tīng)顟B(tài) batchingStrategy.batchedUpdates(method, component);
在組件渲染狀態(tài)isBatchingUpdates中,任何的setState都不會(huì)觸發(fā)更新,而是進(jìn)入隊(duì)列。除此之外,通過(guò)setTimeout/setInterval產(chǎn)生的異步調(diào)用是可以同步更新state的。這樣的講解比較抽象,我們可以直接根據(jù)以下源碼開始理解。
setState下面我們來(lái)看下setState在源碼中的定義:
/** * Sets a subset of the state. Always use this to mutate * state. You should treat `this.state` as immutable. * * There is no guarantee that `this.state` will be immediately updated, so * accessing `this.state` after calling this method may return the old value. * * There is no guarantee that calls to `setState` will run synchronously, * as they may eventually be batched together. You can provide an optional * callback that will be executed when the call to setState is actually * completed. * * When a function is provided to setState, it will be called at some point in * the future (not synchronously). It will be called with the up to date * component arguments (state, props, context). These values can be different * from this.* because your function may be called after receiveProps but before * shouldComponentUpdate, and this new state, props, and context will not yet be * assigned to this. * * @param {object|function} partialState Next partial state or function to * produce next partial state to be merged with current state. * @param {?function} callback Called after state is updated. * @final * @protected */ ReactComponent.prototype.setState = function (partialState, callback) { this.updater.enqueueSetState(this, partialState); if (callback) { this.updater.enqueueCallback(this, callback, "setState"); } };
根據(jù)源碼中的注釋,有這么一句話。
There is no guarantee that this.state will be immediately updated, so accessing this.state after calling this method may return the old value.
大概意思就是setState不能確保實(shí)時(shí)更新state,官方從來(lái)沒(méi)有說(shuō)過(guò)setState是一種異步操作,但也沒(méi)有否認(rèn),只是告訴我們什么時(shí)候會(huì)觸發(fā)同步操作,什么時(shí)候是異步操作。所以我們工作中千萬(wàn)不要被一些民間偏方蒙蔽雙眼,多看看源代碼,發(fā)現(xiàn)原理的同時(shí),還可以發(fā)現(xiàn)很多好玩的東西,開源庫(kù)的好處就是在于我們能在源碼中發(fā)現(xiàn)真理。
我們?cè)谠创a的這段注釋里也能看到setState的一些有趣玩法,比如
// 在回調(diào)中操作更新后的state this.setState({ count: 1 }, function () { console.log("# next State", this.state); }); // 以非對(duì)象的形式操作 this.setState((state, props, context) => { return { count: state.count + 1 } });
回到正題,源碼中setState執(zhí)行了this.updater.enqueueSetState方法和this.updater.enqueueCallback方法 ,暫且不論enqueueCallback,我們關(guān)注下enqueueSetState的作用。
enqueueSetState下面是enqueueSetState的源碼:
/** * Sets a subset of the state. This only exists because _pendingState is * internal. This provides a merging strategy that is not available to deep * properties which is confusing. TODO: Expose pendingState or don"t use it * during the merge. * * @param {ReactClass} publicInstance The instance that should rerender. * @param {object} partialState Next partial state to be merged with state. * @internal */ enqueueSetState: function (publicInstance, partialState) { var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, "setState"); if (!internalInstance) { return; } var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []); queue.push(partialState); enqueueUpdate(internalInstance); }
enqueueSetState如其名,是一個(gè)隊(duì)列操作,將要變更的state統(tǒng)一插入隊(duì)列,待一一處理。隊(duì)列數(shù)據(jù)_pengdingStateQueue會(huì)掛載在一個(gè)組件對(duì)象上internalInstance,對(duì)于internalInstance想要了解下的同學(xué),可以參考下react源碼中的ReactInstanceMap這個(gè)概念。
隊(duì)列操作完成之后,就開始真正的更新操作了。
enqueueUpdate更新方法enqueueUpdate的源碼如下:
/** * Mark a component as needing a rerender, adding an optional callback to a * list of functions which will be executed once the rerender occurs. */ function enqueueUpdate(component) { ensureInjected(); // Various parts of our code (such as ReactCompositeComponent"s // _renderValidatedComponent) assume that calls to render aren"t nested; // verify that that"s the case. (This is called by each top-level update // function, like setProps, setState, forceUpdate, etc.; creation and // destruction of top-level components is guarded in ReactMount.) if (!batchingStrategy.isBatchingUpdates) { batchingStrategy.batchedUpdates(enqueueUpdate, component); return; } dirtyComponents.push(component); }
第一次執(zhí)行setState的時(shí)候,可以進(jìn)入if語(yǔ)句,遇到里面的return語(yǔ)句,終止執(zhí)行。如果不是正處于創(chuàng)建或更新組件階段,則處理update事務(wù)。
第二次執(zhí)行setState的時(shí)候,進(jìn)入不了if語(yǔ)句,將組件放入dirtyComponents。如果正在創(chuàng)建或更新組件,則暫且先不處理update,只是將組件放在dirtyComponents數(shù)組中。
enqueueUpdate包含了React避免重復(fù)render的邏輯。參考源碼中batchedUpdates的調(diào)用情況,mountComponent和updateComponent方法在執(zhí)行的最開始,會(huì)調(diào)用到batchedUpdates進(jìn)行批處理更新,這些是react實(shí)例的生命周期,此時(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。
聽(tīng)不懂?聽(tīng)不懂沒(méi)關(guān)系。。我們會(huì)一句句剖析。
enqueueUpdate和batchingStrategy的概念我們放一起考慮。
batchingStrategy簡(jiǎn)單直譯叫做批量處理策略。這個(gè)是React處理批量state操作時(shí)的精髓,源碼如下:
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; ReactDefaultBatchingStrategy.isBatchingUpdates = true; // The code is written this way to avoid extra allocations if (alreadyBatchingUpdates) { callback(a, b, c, d, e); } else { transaction.perform(callback, null, a, b, c, d, e); } } };
如enqueueUpdate源碼中所述,每次執(zhí)行更新前,會(huì)預(yù)先判斷isBatchingUpdates是否處理批量更新?tīng)顟B(tài),如我們常見(jiàn)的周期諸如componentWillMount、componentDidMount,都是處于isBatchingUpdates的批量更新?tīng)顟B(tài),此時(shí)執(zhí)行的setState操作,不會(huì)進(jìn)入if語(yǔ)句執(zhí)行update,而是進(jìn)入dirtyComponents的堆棧中。
這就是文章開頭所說(shuō)的栗子,為什么setTimeout執(zhí)行的setState會(huì)同步更新state,而react生命周期中執(zhí)行的setState只能異步更新的原因。只有react控制下的事件周期,會(huì)執(zhí)行batchedUpdates切換isBatchingUpdates狀態(tài),保證批量操作能被截獲并插入堆棧。其他事件都和同步執(zhí)行update方法無(wú)異。
執(zhí)行batchedUpdates之后,會(huì)立即將isBatchingUpdates賦值為true,表明此時(shí)即將進(jìn)入更新?tīng)顟B(tài),所有之后的setState進(jìn)入隊(duì)列等待。
這里我們以普通的setTimeout為例,執(zhí)行一次更新。業(yè)務(wù)代如下:
setTimeout(function () { this.setState({ count: this.state.count + 1 }); }, 0);
執(zhí)行時(shí)isBatchingUpdates默認(rèn)是false,所以當(dāng)我們執(zhí)行到batchedUpdates這一步的時(shí)候,源碼中alreadyBatchingUpdates被賦值為false,我們會(huì)跳過(guò)if進(jìn)入else條件,執(zhí)行下一階段transaction.perform。
transaction.performperform為我們執(zhí)行了UI更新的第一步預(yù)操作。這里我們會(huì)執(zhí)行一系列更新初始化操作和更新?tīng)顟B(tài)的關(guān)閉。該方法做了try-catch控制,大量數(shù)據(jù)操作有可能引發(fā)錯(cuò)誤exception,perform方法在這里對(duì)錯(cuò)誤做了截獲控制。
/** * Executes the function within a safety window. Use this for the top level * methods that result in large amounts of computation/mutations that would * need to be safety checked. The optional arguments helps prevent the need * to bind in many cases. * * @param {function} method Member of scope to call. * @param {Object} scope Scope to invoke from. * @param {Object?=} a Argument to pass to the method. * @param {Object?=} b Argument to pass to the method. * @param {Object?=} c Argument to pass to the method. * @param {Object?=} d Argument to pass to the method. * @param {Object?=} e Argument to pass to the method. * @param {Object?=} f Argument to pass to the method. * * @return {*} Return value from `method`. */ perform: function (method, scope, a, b, c, d, e, f) { !!this.isInTransaction() ? "development" !== "production" ? invariant(false, "Transaction.perform(...): Cannot initialize a transaction when there " + "is already an outstanding transaction.") : invariant(false) : void 0; var errorThrown; var ret; try { this._isInTransaction = true; // Catching errors makes debugging more difficult, so we start with // errorThrown set to true before setting it to false after calling // close -- if it"s still set to true in the finally block, it means // one of these calls threw. errorThrown = true; this.initializeAll(0); 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. this.closeAll(0); } } finally { this._isInTransaction = false; } } return ret; }
源碼中執(zhí)行了一些錯(cuò)誤的預(yù)判,最終我們真正執(zhí)行的是closeAll方法。關(guān)于state的數(shù)據(jù)更新,從close開始。
close/** * Invokes each of `this.transactionWrappers.close[i]` functions, passing into * them the respective return values of `this.transactionWrappers.init[i]` * (`close`rs that correspond to initializers that failed will not be * invoked). */ closeAll: function (startIndex) { !this.isInTransaction() ? "development" !== "production" ? invariant(false, "Transaction.closeAll(): Cannot close transaction when none are open.") : invariant(false) : void 0; var transactionWrappers = this.transactionWrappers; for (var i = startIndex; i < transactionWrappers.length; i++) { var wrapper = transactionWrappers[i]; var initData = this.wrapperInitData[i]; var errorThrown; try { // Catching errors makes debugging more difficult, so we start with // errorThrown set to true before setting it to false after calling // close -- if it"s still set to true in the finally block, it means // wrapper.close threw. errorThrown = true; if (initData !== Transaction.OBSERVED_ERROR && wrapper.close) { wrapper.close.call(this, initData); } errorThrown = false; } finally { if (errorThrown) { // The closer for wrapper i threw an error; close the remaining // wrappers but silence any exceptions from them to ensure that the // first error is the one to bubble up. try { this.closeAll(i + 1); } catch (e) {} } } } this.wrapperInitData.length = 0; }
在介紹close之前,我們先了解下兩個(gè)對(duì)象。也就是源碼中的this.transactionWrappers。他在初始被賦值為[FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES],也就是以下兩個(gè)對(duì)象,在源碼被稱作為wrapper。
var RESET_BATCHED_UPDATES = { initialize: emptyFunction, close: function () { ReactDefaultBatchingStrategy.isBatchingUpdates = false; } }; var FLUSH_BATCHED_UPDATES = { initialize: emptyFunction, close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates) };
源碼中我們看到closeAll執(zhí)行了一次for循環(huán),并執(zhí)行了每個(gè)wrapper的close方法。
RESET_BATCHED_UPDATES的close方法很簡(jiǎn)單,把isBatchingUpdates更新中這個(gè)狀態(tài)做了一個(gè)close的操作,也就是賦值為false,表明本次批量更新已結(jié)束。
FLUSH_BATCHED_UPDATES的close方法執(zhí)行的是flushBatchedUpdates方法。
flushBatchedUpdatesvar flushBatchedUpdates = function () { // ReactUpdatesFlushTransaction"s wrappers will clear the dirtyComponents // array and perform any updates enqueued by mount-ready handlers (i.e., // componentDidUpdate) but we need to check here too in order to catch // updates enqueued by setState callbacks and asap calls. while (dirtyComponents.length || asapEnqueued) { if (dirtyComponents.length) { var transaction = ReactUpdatesFlushTransaction.getPooled(); transaction.perform(runBatchedUpdates, null, transaction); ReactUpdatesFlushTransaction.release(transaction); } if (asapEnqueued) { asapEnqueued = false; var queue = asapCallbackQueue; asapCallbackQueue = CallbackQueue.getPooled(); queue.notifyAll(); CallbackQueue.release(queue); } } };
我們暫且不論asap是什么,可以看到flushBatchedUpdates做的是對(duì)dirtyComponents的批量處理操作,對(duì)于隊(duì)列中的每個(gè)component執(zhí)行perform更新。這些更新都會(huì)執(zhí)行真正的更新方法runBatchedUpdates。
function runBatchedUpdates(transaction) { var len = transaction.dirtyComponentsLength; !(len === dirtyComponents.length) ? "development" !== "production" ? invariant(false, "Expected flush transaction"s stored dirty-components length (%s) to " + "match dirty-components array length (%s).", len, dirtyComponents.length) : invariant(false) : void 0; // Since reconciling a component higher in the owner hierarchy usually (not // always -- see shouldComponentUpdate()) will reconcile children, reconcile // them before their children by sorting the array. dirtyComponents.sort(mountOrderComparator); for (var i = 0; i < len; i++) { // If a component is unmounted before pending changes apply, it will still // be here, but we assume that it has cleared its _pendingCallbacks and // that performUpdateIfNecessary is a noop. var component = dirtyComponents[i]; // If performUpdateIfNecessary happens to enqueue any new updates, we // shouldn"t execute the callbacks until the next render happens, so // stash the callbacks first 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.props === component._renderedComponent._currentElement) { namedComponent = component._renderedComponent; } markerName = "React update: " + namedComponent.getName(); console.time(markerName); } ReactReconciler.performUpdateIfNecessary(component, transaction.reconcileTransaction); if (markerName) { console.timeEnd(markerName); } if (callbacks) { for (var j = 0; j < callbacks.length; j++) { transaction.callbackQueue.enqueue(callbacks[j], component.getPublicInstance()); } } } }
runBatchedUpdates中的核心處理是ReactReconciler.performUpdateIfNecessary。
/** * If any of `_pendingElement`, `_pendingStateQueue`, or `_pendingForceUpdate` * is set, update the component. * * @param {ReactReconcileTransaction} transaction * @internal */ performUpdateIfNecessary: function (transaction) { if (this._pendingElement != null) { ReactReconciler.receiveComponent(this, this._pendingElement, transaction, this._context); } if (this._pendingStateQueue !== null || this._pendingForceUpdate) { this.updateComponent(transaction, this._currentElement, this._currentElement, this._context, this._context); } }
在這里我們終于又看到了我們熟悉的_pendingStateQueue,還記得這是什么嗎?是的,這就是state的更新隊(duì)列,performUpdateIfNecessary做了隊(duì)列的特殊判斷,避免導(dǎo)致錯(cuò)誤更新。
接下來(lái)的這段代碼是updateComponent,源碼內(nèi)容比較長(zhǎng),但是我們可以看到很多熟知的生命周期方法的身影,比如說(shuō)componentWillReceiveProps和shouldComponentUpdate,做了component的更新判斷。
這部分方法統(tǒng)一歸屬于ReactCompositeComponentMixin模塊,有興趣了解整個(gè)生命周期的同學(xué)可以參考下源碼中的該模塊源碼,這里我們不再擴(kuò)展,會(huì)繼續(xù)講解state的更新過(guò)程。updateComponent
/** * Perform an update to a mounted component. The componentWillReceiveProps and * shouldComponentUpdate methods are called, then (assuming the update isn"t * skipped) the remaining update lifecycle methods are called and the DOM * representation is updated. * * By default, this implements React"s rendering and reconciliation algorithm. * Sophisticated clients may wish to override this. * * @param {ReactReconcileTransaction} transaction * @param {ReactElement} prevParentElement * @param {ReactElement} nextParentElement * @internal * @overridable */ updateComponent: function (transaction, prevParentElement, nextParentElement, prevUnmaskedContext, nextUnmaskedContext) { var inst = this._instance; var willReceive = false; var nextContext; var nextProps; // Determine if the context has changed or not if (this._context === nextUnmaskedContext) { nextContext = inst.context; } else { nextContext = this._processContext(nextUnmaskedContext); willReceive = true; } // Distinguish between a props update versus a simple state update if (prevParentElement === nextParentElement) { // Skip checking prop types again -- we don"t read inst.props to avoid // warning for DOM component props in this upgrade nextProps = nextParentElement.props; } else { nextProps = this._processProps(nextParentElement.props); 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) { inst.componentWillReceiveProps(nextProps, nextContext); } var nextState = this._processPendingState(nextProps, nextContext); var shouldUpdate = this._pendingForceUpdate || !inst.shouldComponentUpdate || inst.shouldComponentUpdate(nextProps, nextState, nextContext); if ("development" !== "production") { "development" !== "production" ? warning(shouldUpdate !== undefined, "%s.shouldComponentUpdate(): Returned undefined instead of a " + "boolean value. Make sure to return true or false.", this.getName() || "ReactCompositeComponent") : void 0; } 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. this._currentElement = nextParentElement; this._context = nextUnmaskedContext; inst.props = nextProps; inst.state = nextState; inst.context = nextContext; } }
跳過(guò)除了state的其他源碼部分,我們可以看到該方法中仍然嵌套了一段對(duì)state的更新方法,這個(gè)方法就是state更新的終點(diǎn)_processPendingState。
_processPendingState為什么對(duì)state中的同一屬性做多次setState處理,不會(huì)得到多次更新?比如
this.setState({ count: count++ }); this.set
那是因?yàn)樵创a中的多個(gè)nextState的更新,只做了一次assign操作,如下源碼請(qǐng)查看:
_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]; _assign(nextState, typeof partial === "function" ? partial.call(inst, nextState, props, context) : partial); } return nextState; }
有人說(shuō),React抽象來(lái)說(shuō),就是一個(gè)公式
UI=f(state).
的確如此,一個(gè)簡(jiǎn)單的setState執(zhí)行過(guò)程,內(nèi)部暗藏了這么深的玄機(jī),經(jīng)歷多個(gè)模塊的處理,經(jīng)歷多個(gè)錯(cuò)誤處理機(jī)制以及對(duì)數(shù)據(jù)邊界的判斷,保證了一次更新的正常進(jìn)行。同時(shí)我們也發(fā)現(xiàn)了為什么setState的操作不能簡(jiǎn)單的說(shuō)作是一個(gè)異步操作,大家應(yīng)該在文章中已經(jīng)找到了答案。
對(duì)其他react深層的理解,感興趣的同學(xué)可以多多參考下源碼。本文參考react源碼版本為15.0.1。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/108785.html
摘要:只涉及了,其他均沒(méi)有自己實(shí)現(xiàn)。這種組件的復(fù)用性是最強(qiáng)的。所以會(huì)新建,只繼承的原型,不包括,以此來(lái)節(jié)省內(nèi)存。 showImg(https://segmentfault.com/img/remote/1460000019783989); 一、React.Component() GitHub:https://github.com/AttackXiaoJinJin/reactExplain/...
摘要:首先是創(chuàng)建了一個(gè)構(gòu)造函數(shù),他的原型指到的原型然后創(chuàng)建了一個(gè)加上了和一樣的屬性這里為啥不用。的原型指向的實(shí)例修改原型的屬性使其正確指向的構(gòu)造函數(shù),并掛一個(gè)的屬性。 每次都信誓旦旦的給自己立下要好好學(xué)習(xí)react源碼的flag,結(jié)果都是因?yàn)槟硞€(gè)地方卡住了,或是其他原因沒(méi)看多少就放棄了。這次又給自己立個(gè)flag-堅(jiān)持看完react源碼。為了敦促自己,特開設(shè)這樣一個(gè)專欄來(lái)記錄自己的學(xué)習(xí)歷程,這...
摘要:生命周期當(dāng)首次掛載組件時(shí),按順序執(zhí)行和。由于是通過(guò)構(gòu)造函數(shù)進(jìn)行管理的,所以也是整個(gè)生命周期中先開始執(zhí)行的加載組件若存在,則執(zhí)行。一組及方法稱為一個(gè)。因此,允許用戶通過(guò)來(lái)判斷該組件是否需要進(jìn)行算法分析。 生命周期 ? 當(dāng)首次掛載組件時(shí),按順序執(zhí)行 getDefaultProps、getInitialState、componentWillMount、 render 和 componentD...
摘要:新的值回調(diào)函數(shù)。官方注解是給組件做個(gè)標(biāo)記需要重新渲染,并且將可選的回調(diào)函數(shù)添加到函數(shù)列表中,這些函數(shù)將在重新渲染的時(shí)候執(zhí)行。一共做了兩件事一是通過(guò)執(zhí)行方法來(lái)更新組件二是若方法傳入了回調(diào)函數(shù)則將回調(diào)函數(shù)存入隊(duì)列。 Q1 setState改變狀態(tài)之后,不會(huì)立即更新state值。所以,如果改變state值,react是什么時(shí)候進(jìn)行組件的更新呢?setState()到底做了一些什么呢? A1 ...
摘要:就是,如果你不了解這個(gè)的話可以閱讀下相關(guān)文檔,是應(yīng)用初始化時(shí)就會(huì)生成的一個(gè)變量,值也是,并且這個(gè)值不會(huì)在后期再被改變。這是我的剖析 React 源碼的第三篇文章,如果你沒(méi)有閱讀過(guò)之前的文章,請(qǐng)務(wù)必先閱讀一下 第一篇文章 中提到的一些注意事項(xiàng),能幫助你更好地閱讀源碼。 文章相關(guān)資料 React 16.8.6 源碼中文注釋,這個(gè)鏈接是文章的核心,文中的具體代碼及代碼行數(shù)都是依托于這個(gè)倉(cāng)庫(kù) 熱身...
閱讀 990·2021-10-13 09:39
閱讀 3812·2021-10-12 10:12
閱讀 1970·2021-08-13 15:07
閱讀 1086·2019-08-29 15:31
閱讀 2961·2019-08-26 13:25
閱讀 1861·2019-08-23 18:38
閱讀 1973·2019-08-23 18:25
閱讀 1923·2019-08-23 17:20