寫React也有段時間了,一直也是用Redux管理數據流,最近正好有時間分析下源碼,一方面但願對Redux有一些理論上的認識;另外一方面也學習下框架編程的思惟方式。react
一、Redux經過全局惟一的store對象管理項目中的stategit
var store = createStore(reducer,initialState);
二、能夠經過store註冊listener,註冊的listener會在store tree每次變動後執行github
store.subscribe(function () { console.log("state change"); });
一、store調用dispatch
,經過action把變動的信息傳遞給reducer編程
var action = { type: 'add'}; store.dispatch(action);
二、store根據action攜帶type
在reducer中查詢變動具體要執行的方法,執行後返回新的stateredux
export default (state = initialState, action)=>{ switch (action.type) { case 'add': return { count:state.count + 1 } break; default: break; } }
三、reducer執行後返回的新狀態會更新到store tree中,觸發由store.subscribe()
註冊的全部listener數組
主要方法:閉包
查看完整createStore
請戳這裏
createStore
方法用來註冊一個store,返回值爲包含了若干方法的對象,方法體以下:app
export var ActionTypes = { INIT: '@@redux/INIT' } export default function createStore(){ function getState(){} function dispatch(){} function subscribe(){} function replaceReducer(){} dispatch({ type: ActionTypes.INIT }) return { dispatch, subscribe, getState, replaceReducer } }
下面逐個代碼段分析功能
createStore
完整函數聲明以下:框架
createStore( reducer:(state, action)=>nextState, preloadedState:any, enhancer:(store)=>nextStore )=>{ getState:()=>any, subscribe:(listener:()=>any)=>any, dispatch:(action:{type:""})=>{type:""}, replaceReducer:(nextReducer:(state, action)=>nextState)=>void }
能夠看出整個函數是一個閉包結構。參數有三個,返回值公開出若干方法異步
固然,createStore
內部處理了其重載形式,即:能夠不傳preloadedState
createStore( reducer:(state, action)=>nextState, enhancer:(store)=>nextStore )
參數:
進入createSore
首先執行以下操做:
createStore(reducer,preloadedState,enhancer)
和createStore(reducer,enhancer)
重點分析下50行:
return enhancer(createStore)(reducer, preloadedState)
本語句執行了外部傳入的enhancer,接收舊createStore
,返回一個新createStore
並執行,此過程造成一次遞歸;
那麼遞歸何時中止呢?
能夠看到,新createStore
執行時,僅有reducer和preloadedState兩個參數,再次運行到45行時,不會進入if條件 故不會再造成第二次遞歸,此時遞歸中止;
理論上,createStore
僅被加強了一次,那若是但願對其進行屢次加強該怎麼辦呢?
Redux提供了compose
和applyMiddleWare
方法,用來在Store上註冊中間件,由此來實現屢次加強。
getState
方法比較簡單,直接返回當前store tree狀態
currentReducer
、currentState
聲明當前reducer方法集合和store tree狀態,初始值爲外部傳入的createStore
參數,currentListeners
和nextListeners
定義了存放store變化時要執行響應函數的數組集合Redux採用了觀察者模式,store內部維護listener數組,用於存儲全部經過store.subscrib
註冊的listener,store.subscrib
返回unsubscrib
方法,用於註銷當前listener;當store tree更新後,依次執行數組中的listener
具體代碼以下:
dispatch
方法主要完成兩件事:
一、根據action查詢reducer中變動state的方法,更新store tree
二、變動store tree後,依次執行listener中全部響應函數
有個問題須要注意:
方法中使用了全局定義的isDispatching
用於給變動中的store tree加鎖;即:只有當本次store tree變動完畢後,才容許執行下一次變動,避免store tree響應多個變動時,結果不一樣步的問題;但事實上,這種寫法也決定了,目前的store tree只能響應同步變動(異步變動須要經過添加中間件實現)
replaceReducer
用於替換操做store tree中state的方式
整個方法代碼量很少,從外部接收新的reducer方法後,替換掉內部舊的ruducer。
須要注意一下199行的dispatch
方法,這一行主動觸發了一次變動。因爲每次dispatch
執行後,redux都會執行reducer或子reducer方法(若是使用了combineReducers
),因此這一行的做用就是在初始化store tree中全部的state節點。
以上就是整個createStore
方法的主要實現過程,其中dispatch
方法爲控制整個store tree變動的核心方法。觸發store tree變動的方式只有一個,就是dispatch
一個action
結合上面store tree變動的過程,咱們能夠看到,真正致使變動的核心代碼就是:
currentState = currentReducer(currentState, action)
試想,若整個項目只經過一個reducer方法維護整個store tree,隨着項目功能和複雜度的增長,咱們須要維護的store tree層級也會愈來愈深,當咱們須要變動一個處於store tree底層的state,reducer中的變動邏輯會十分複雜且臃腫。
而combineReducers
存在的目的就是解決了整個store tree中state與reducer一對一設置的問題。咱們能夠根據項目的須要,定義多個子reducer方法,每一個子reducer僅維護整個store tree中的一部分state, 經過combineReducers
將子reducer合併爲一層。這樣咱們就能夠根據實際須要,將整個store tree拆分紅更細小的部分,分開維護。
combineReducers
完整代碼請戳這裏
整個函數體結構以下:
combineReducers( reducers:Object )=> reducer( state:any, action:{type:""} )
參數reducers是一個Object對象,其中包含全部待合併的子reducer方法
返回值是合併後的reducer方法,在執行此方法時,會在已合併的全部子reducer中查詢要執行的reducer,並執行,變動其對應的state片斷。
下面逐個代碼段分析具體實現:
以上這部分主要用於規範化存儲子ruducer的reducers對象
finalReducers
對象中assertReducerSanity
方法校驗全部子reducer的初始值和執行後結果是否爲空,是則提示錯誤。以上這段代碼爲combineReducers
的核心代碼,其返回一個function,用於查詢真正要變動的state片斷
finalReducers
,獲取到當前key對應的子reducer和子state,執行reducer獲得當前state片斷更新後的狀態,並更新到整個store tree中。previousStateForKey
時,值可能爲undefined,那麼接下來執行var nextStateForKey = reducer(previousStateForKey, action)
其實是依次爲每一個子state片斷進行初始化。nextStateForKey !== previousStateForKey
直接經過引用關係判斷state是否變動。故必定要注意,定義reducer方法時,必定要遵循函數式編程,確保傳入的state與返回的state不要存在引用關係,不然可能致使store tree中狀態沒法更新。至此,咱們能夠看到combineReducers
方法,實際就是在每次要執行reducer時,經過action.type定義的類型進行查詢,得到子reducer並執行。
經過以上分析,咱們須要注意兩個問題:
一、子reducer遵循函數式編程,不要直接變動做爲參數傳入的state,變動state後,必定要返回一個新state對象,不要跟參數state創建引用關係(可使用Immutable處理state)
二、因爲combineReducers
內部僅經過action.type做爲查詢當前要執行的子reducer的依據,會更新全部查詢到的state片斷,故不建議子reducer中,action.type的值出現重複,不然可能會誤更新state。
本篇經過分析源碼整理了Redux中Store對象的執行邏輯,重點分析了dispatch(action)後,store tree內部狀態如何更新。 篇幅所限,沒有分析如何在store上註冊中間件,以及如何在store tree變動後,觸發頁面更新的過程,這些會在以後的博客中更新 第一次寫源碼分析還有不少不足,若有錯誤,歡迎指正。