????“整個應用的 state被儲存在一棵 object tree 中,并且這個 object tree 只存在于唯一一個store?中。”這是Redux官方文檔給出三大原則的第一條,著重強調了單一數據源這個概念其中,單一數據源的一個重要的前提,就是有唯一一個store,而這個store,正是被我們今天要講的核心API——createStore創建的,至于我們擔心數據過于龐大的問題,之前在概覽中提到的API——combineReducers正是用來解決這個問題的。
createStore.js概覽
? ?createStore可謂是整個Redux的靈魂。基本上Redux的核心功能已經全部被createStore以及createStore生成的store中,下面我們就來了解一下createStore究竟是怎么工作的,createStore的源碼比較長,我們分兩段貼出︿( ̄︶ ̄)︿:
這段可以看到,createStore接受3個參數:reducer,?preloadedState,?enhancer。reducer我們接下來會具體介紹,這里不過多贅述,第二個參數是preloadedState,它是state的初始值,實際上他并不僅僅是扮演著一個initialState的角色,如果我們第二個參數是函數類型,createStore會認為你忽略了preloadedState而傳入了一個enhancer,如果我們傳入了一個enhancer,createStore會返回enhancer(createStore)(reducer, preloadedState)的調用結果,這是常見高階函數的調用方式。在這個調用中enhancer接受createStore作為參數,對createStore的能力進行增強,并返回增強后的createStore。然后再將reducer和preloadedState作為參數傳給增強后的createStore,最終得到生成的store。
這段我們省略了dispatch,subscribe,getState,replaceReducer,[$$observable]: observable的代碼,因為我們會在接下來去依次剖析他們,這里也可以看出,createStore的返回值是dispatch,subscribe,getState,replaceReducer,[$$observable]: observable,它們共同組成了一個store,實際上它本身只暴露出來四個方法dispatch,subscribe,getState,replaceReducer,而[$$observable]: observable是供Redux內部使用的,有興趣的童鞋可以自己去研究(傳送門),本文就不再贅述。稍后我們將會介紹這四個我們可以使用到的API。
1.action
這里插一個action,action代表的是用戶的操作。redux規定action一定要包含一個type屬性,且type屬性也要唯一,相同的type,redux視為同一種操作,因為處理action的函數reducer只判斷action中的type屬性。
2. reducer
Redux中負責響應action并修改數據的角色就是reducer,reducer的本質實際上是一個函數,其函數簽名為:reducer(previousState,action)?=> newState。可以看出,reducer 接受兩個參數,previousState以及action函數返回的action對象,并返回最新的state,實際上reducer在處理previousState還需要一個特殊的非空判斷,以下是一個Dome:
reducer 只是一個模式匹配的東西,真正處理數據的函數,一般是額外在別的地方寫的(當然直接寫在reducer中也沒問題,只是不利于后期維護),在 reducer 中調用罷了。reducer 為什么叫 reducer 呢?因為 action 對象各種各樣,每種對應某個 case ,但最后都匯總到 state 對象中,從多到一,這是一個減少( reduce )的過程,所以完成這個過程的函數叫 reducer。
3.getState
源碼如下:
可以看出,這里定義了4個本地變量:
????1、currentReducer:當前的reducer,支持用過store.replaceReducer方式動態替換reducer,為代碼熱替換提供了可能。
????2、currentState:應用當前的狀態,默認為初始化狀態。
????3、listeners:當前監聽store的監聽器
????4、isDispatching:某個action是否處于分發處理過程中。
4.subscribe
在getSate之后,我們又定義了subscribe:
subscribe接收一個listener,它的作用是給store添加監聽函數。nextListeners儲存了整個監聽函數列表。?subscribe的返回值是一個unsubscribe,是一個解綁函數,調用該解綁函數,會將已經添加的監聽函數刪除,該監聽函數處于一個閉包之中,會一直存在,所以在解綁函數中能刪除該監聽函數。(由此可見redux源碼設計的精巧,多處地方巧用閉包,精簡了許多代碼。)
你可能會奇怪,好像我們在Redux中并沒有使用過subscribe方法,實際上React Redux 中的 connect方法隱式地幫我們完成了這個工作。
5.dispatch
? ?接下來,要說的是store非常核心的一個方法,也是我們在應用中最常使用的方法dispatch。他的源碼如下:
? ?首先,我們校驗了action是否為一個原生js對象,接下來為我們校驗了action對象是否包含了必要的type字段。再接下來,判斷當前是否處于某個action分發過程中,這個檢查主要是為了避免在reducer中分發action。
????在一系列檢查完畢后,若均沒有問題,將當前的狀態和action傳給當前reducer,用于生成新的state。在得到新的狀態后,依次調用所有的監聽器,通知狀態變更。需要注意的是,我們在通知監聽器變更發生時,并沒有將最新的狀態傳給監聽器。這是因為在監聽器中我們可以直接調用store.getState()方法拿到最新的狀態,最后,獎處理之后的action返回。
6.replaceReducer? ??
replaceReducer是替換當前的reducer的函數,replaceReducer接受一個新的reducer,替換完成之后,會執行?dispatch({ type: ActionTypes.INIT })?,用來初始化store的狀態。官方舉出了三種replaceReducer的使用場景,分別是:
? ?1、當你的程序要進行代碼分割的時候
? ? 2、當你要動態的加載不同的reducer的時候
? ? 3、當你要實現一個實時reloading機制的時候
至此,這個至關重要的“靈魂”API——createStore已經基本介紹完了,到目前為止,我們都在一個清晰的同步數據流中跑動,但是實際開發過程中,這遠遠不能滿足我們的需求,比如我要發送一個請求,等數據返回后再更新View怎么辦?這就需要我們使用Redux middleware了,下一章,將為大家介紹Redux中間件middleware,這也是我本人非常喜歡的一塊代碼,代碼之簡潔,思維之精妙真的是很令人受教,敬請期待下一章~( ̄▽ ̄~)(~ ̄▽ ̄)~。