前端框架從MVC過渡到MVVM。從DOM操作到數據驅動,一直在不斷的進步着,提升着,
angular中用的是watcher對象,vue是觀察者模式,react就是state了,他們各有各的特點,沒有好壞之分,只有需求不同而選擇不同。
今天就着重詳細的隨手寫點我對react中state的理解:
React通過管理狀態實現對組件的管理,通過this.state()方法更新state。當this.setState()被調用的時候,React會重新調用render方法來重新渲染UI。
在說setstate這個磨人的小妖精之前,不得不先說一下state這個小可愛了
定義一個合適的State,是正確創建組件的第一步。因為有一些變量不需要響應式的使用,如果使用了state,就會給這個變量增加一些響應式掛載,要時 刻 記得做到完美 ^-^
判斷是否可以做為一個state的條件:
1、變量如果是通過props從父組件中獲取,就不是一個狀態
2、如果這個變量可以通過其他的狀態state或者屬性props 通過數據處理得到,就不是一個狀態
3、如果變量在render中沒有使用到,那就不是一個state
4、變量在整個生命周期中都保持不變時,也不是一個狀態
其實使用的時候最多的使用到的就是state和props,他們兩個是有很大的區別的,最主要的區別就是:
State是可變的,是組件內部維護的一組用於反映組件UI變化的狀態集合;
而Props對於使用它的組件來說,是只讀的,要想修改Props,只能通過該組件的父組件修改。在組件狀態上移的場景中,父組件正是通過子組件的Props, 傳遞給子組件其所需要的狀態。
在使用state的時候, 如果我們企圖直接修改state中的某一個值之后直接打印(使用)他,就會發現,他其實並沒有改變。
就像下面的例子,企圖通過點擊事件之后就使用修改之后的state的值,但是會發state中的並沒有被立即修改,還是原先的值,我們都知道那是因為 setState就相當於是一個異步操作,不能立即被修改
那么我們也都知道為了解決上面的問題會有很多方法例如:
方法一:
這個回調函數會在修改了state之后才會執行,這就就可以happy的使用修改之后的state的值了
方法二:
操作異步函數,用的最舒服的還是async / await 啦
當然還有很多其他的解決辦法啦。。。。。。。。只是我會比較常用這兩種方法而已
在使用setState的時候,有兩種格式;
第一種setstate()格式 第一個參數是一個對象,第二個參數是一個回調函數,這個回調函數是在setstate執行完並頁面渲染了之后再執行
但是這種修改的方式不穩妥,因為是直接修改,我還是比較喜歡使用第二種格式
setstate的第二種格式,接收一個回調函數,而不是一個對象,這個回調函數有兩個參數,
一個是接收前一個狀態值作為第一個參數,並將更新后的值作為第二個參數
這種寫法在這個例子里有點大材小用了,但是在處理復雜數據和邏輯的時候會特別好用 !
總的來說setstate這個磨人的小妖精就和Vue中的數據響應一樣,
在Vue中,
Vue官網上偷的圖。。。。。。。。。。
組件、函數等渲染---->創建一個虛擬DOM樹------->當data、computed、props改變時會引起頁面的刷新--------watcher檢測變化,當變化以后不會 立 即渲染,會有一個隊列,只要觀察到數據變化,Vue 將開啟一個隊列,並緩沖在同一事件循環中發生的所有數據改變。如果同一個 watcher 被多次觸 發, 只會被推入到隊列中一次。這種在緩沖時去除重復數據對於避免不必要的計算和 DOM 操作上非常重要。然后,在下一個的事件循環“tick”中,Vue 刷 新隊列並執行實際 (已去重的) 工作。
在react中
可以看出在react中也是和Vue中的一樣,state的值在修改了之后並不會立即被修改,而是也有一個類似的隊列,setState通過一個隊列機制實現state的更新。當執行setState時,會把需要更新的state合並后放入狀態隊列,而不會立刻更新this.state,利用這個隊列機制可以高效的批量的更新state。
真是一個神奇的方法,很喜歡這個可以高效批量更新state的機制,於是就去瞅了瞅setState的源碼,想看一下react是怎么構造出setState這個磨人的小妖精的
這個構造小妖精的過程也是磨人的。。。。。。理了好久才理清。。。。
它的主要流程如下:
1、當調用setState時,實際上會執行enqueueSetState方法,並對partialState以及_pendingStateQueue更新隊列進行合並,最終通過enqueueUpdate執行state更新
2、 如果組件當前正處於update事務中,則先將Component存入dirtyComponent中。否則調用batchedUpdates處理。
而performUpdateIfNecessary方法獲取_pendingElement、_pendingStateQueue、_pendingForceUpdate,並調用reciveComponent和updateComponent方法進行組件更新。
3、batchedUpdates發起一次transaction.perform()事務
4、開始執行事務初始化,運行,結束三個階段
初始化:事務初始化階段沒有注冊方法,故無方法要執行
運行:執行setSate時傳入的callback方法,一般不會傳callback參數
結束:更新isBatchingUpdates為false,並執行FLUSH_BATCHED_UPDATES這個wrapper中的close方法
5、FLUSH_BATCHED_UPDATES在close階段,會循環遍歷所有的dirtyComponents,調用updateComponent刷新組件,並執行它的pendingCallbacks, 也就是setState中設置的callback。
源碼部分有空補上。。。。。。