天天看點

React源碼分析4 — setState機制1 概述2 setState和replaceState3 enqueueSetState4 事務transaction5 runBatchedUpdates更新元件6 總結

react源碼系列文章,請多支援:

<a href="https://www.atatech.org/articles/72905">react源碼分析1 — 元件和對象的建立(createclass,createelement)</a>

<a href="http://www.atatech.org/articles/72908">react源碼分析2 — react元件插入dom流程</a>

<a href="https://www.atatech.org/articles/73748">react源碼分析3 — react生命周期詳解</a>

<a href="https://www.atatech.org/articles/73749">react源碼分析4 — setstate機制</a>

<a href="https://www.atatech.org/articles/74571">react源碼分析5 -- 元件通信,refs,key,reactdom</a>

<a href="https://www.atatech.org/articles/74572">react源碼分析6 — react合成事件系統</a>

react作為一門前端架構,雖然隻是focus在mvvm中的view部分,但還是實作了view和model的綁定。修改資料的同時,可以實作view的重新整理。這大大簡化了我們的邏輯,隻用關心資料流的變化,同時減少了代碼量,使得後期維護也更加友善。這個特性則要歸功于setstate()方法。react中利用隊列機制來管理state,避免了很多重複的view重新整理。下面我們來從源碼角度探尋下setstate機制。

我們都知道setstate是以修改和新增的方式改變state的,不會改變沒有涉及到的state。而replacestate則用新的state完全替換掉老state。比如

列印如下

可見,setstate不會影響沒有涉及到的state,而replacestate則是完完全全的替換。下面讓我們進入源碼來探尋究竟吧。

setstate方法入口如下

取名為partialstate,有部分state的含義,可見隻是影響涉及到的state,不會傷及無辜。enqueuesetstate是state隊列管理的入口方法,比較重要,我們之後再接着分析。

replacestate中取名為newstate,有完全替換的含義。同樣也是以隊列的形式來管理的。

其中getinternalinstancereadyforupdate源碼如下,解釋都在代碼注釋中

enqueueupdate源碼如下

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中則會。

事務通過wrapper進行封裝。一個wrapper包含一對initialize和close方法。比如reset_batched_updates

transcation被包裝在wrapper中,比如

transaction是通過transaction.perform(callback, args…)方法進入的,它會先調用注冊好的wrapper中的initialize方法,然後執行perform方法中的callback,最後再執行close方法。截取react代碼中的一張圖如下

React源碼分析4 — setState機制1 概述2 setState和replaceState3 enqueueSetState4 事務transaction5 runBatchedUpdates更新元件6 總結

下面分析transaction.perform(callback, args…)

前面分析到enqueueupdate中調用transaction.perform(callback, args...)後,小夥伴們肯定會發現,callback還是enqueueupdate方法啊,那豈不是死循環了?不是說好的setstate會調用updatecomponent,進而自動重新整理view的嗎?别急,我們還是要先從transaction事務說起。

我們的wrapper中注冊了兩個wrapper,如下

reset_batched_updates用來管理isbatchingupdates狀态,我們前面在分析setstate是否立即生效時已經講解過了。那flush_batched_updates用來幹嘛呢?

flush_batched_updates會在一個transaction的close階段運作runbatchedupdates,進而執行update。

runbatchedupdates循環周遊dirtycomponents數組,主要幹兩件事。首先執行performupdateifnecessary來重新整理元件的view,然後執行之前阻塞的callback。下面來看performupdateifnecessary。

最後驚喜的看到了receivecomponent和updatecomponent吧。receivecomponent最後會調用updatecomponent,而updatecomponent中會執行react元件存在期的生命周期方法,如componentwillreceiveprops, shouldcomponentupdate, componentwillupdate,render, componentdidupdate。 進而完成元件更新的整套流程。

setstate流程還是很複雜的,設計也很精巧,避免了重複無謂的重新整理元件。它的主要流程如下

enqueuesetstate将state放入隊列中,并調用enqueueupdate處理要更新的component

如果元件目前正處于update事務中,則先将component存入dirtycomponent中。否則調用batchedupdates處理。

batchedupdates發起一次transaction.perform()事務

開始執行事務初始化,運作,結束三個階段

初始化:事務初始化階段沒有注冊方法,故無方法要執行

運作:執行setsate時傳入的callback方法,一般不會傳callback參數

結束:更新isbatchingupdates為false,并執行flush_batched_updates這個wrapper中的close方法

flush_batched_updates在close階段,會循環周遊所有的dirtycomponents,調用updatecomponent重新整理元件,并執行它的pendingcallbacks, 也就是setstate中設定的callback。

繼續閱讀