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代碼中的一張圖如下
下面分析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。