前面寫了《React組件性能優化》《Redux性能優化》《React-Redux性能優化》,可是都沒有從這些框架的實現上講爲何?此次就從源碼上來分析一下這些框架的實現原理,以更深刻的理解這些框架,並更好的使用它們。redux
Redux的api很簡單,下面一個一個的分析。segmentfault
首先說下它的三個參數reducer、preloadedState、enhancer。reducer是惟一必傳的參數,它很重要,由於它決定了整個state。preloadedState就是state的初始值。第三個參數不是特別經常使用,它是個函數,若是它存在的狀況下,會執行下面的語句:api
enhancer(createStore)(reducer, preloadedState)
很顯然enhancer和middleware做用很像,用來加強store。數組
createStore內部維護了currentReducer(當前的reducer)和currentState(當前的state),初始化的時候:性能優化
currentReducer = reducer currentState = preloadedState
這兩個變量很重要,由於不少操做都和它們相關。app
在createStore內部維護了兩個數組currentListeners、nextListeners。nextListeners的存在是爲了不在listeners執行過程當中,listeners發生改變,致使錯誤。listeners的添加或刪除都是對nextListeners進行操做的。框架
nextListeners = currentListeners.slice()
保證nextListeners的修改不會影響currentListeners。異步
subscribe(listener)的返回值是個函數,執行這個函數就會unsubscribe(listener),取消監聽。函數
很重要,由於只能經過它修改state,它只接收一個參數action
,action必須是簡單對象,並且必須有type屬性。在dispatch內部關鍵代碼是:性能
currentState = currentReducer(currentState, action) var listeners = currentListeners = nextListeners for (var i = 0; i < listeners.length; i++) { listeners[i]() }
第一行用來調用reducer,並將執行的結果賦給currentState。這樣currentState中的數據就老是最新的,即reducer處理完action以後返回的數據。沒有條件,執行dispatch後reducer總會執行。
後面3行代碼是用來調用subscribe傳進來的listeners,按順序執行它們,沒有任何條件判斷,也就是說只要執行dispatch,全部的listeners都會執行,無論state有沒有發生改變,並且listeners執行的時候是沒參數的。
Redux內部有個變量
ActionTypes = { INIT: '@@redux/INIT' }
在建立store(即調用createStore(reducer, preloadedState, enhancer)
)的時候會執行
dispatch({ type: ActionTypes.INIT })
因此reducer和listeners在store建立的時候都會被執行一遍,listeners沒有什麼特別要關注的。reducer執行必須關注,由於它的執行結果影響你的數據。好比createStore的時候你沒有傳入preloadedState,在reducer內的state有默認參數,正常狀況下你的reducer會使用default分支處理這個action,並且通常default分支會直接返回state,因此這種狀況下store建立完後,使用getState()
獲取的值就是默認參數組成的state。
獲取store中的state,很簡單,主要代碼:
return currentState
在沒有dispatch正在執行的狀況下,直接返回前面說很重要的currentState。
參數是nextReducer,也很簡單,關鍵代碼:
currentReducer = nextReducer dispatch({ type: ActionTypes.INIT })
直接拿nextReducer替換掉前面說很重要的currentReducer,後面再執行dispatch,action就會被nextReducer處理,處理的結果賦值給currentState。替換以後會執行一遍初始化action。
代碼雖然看起來很長,可是大多都是用來處理校驗reducer的。它接收的參數reducers是個對象,對象的value不能是undefined,必須是function。符合這個標準的reducer會被放入finalReducers中。
而後再對finalReducers進行校驗,reducer必須有default處理,不能處理Redux內部的action type,好比@@redux/INIT
。而後返回一個函數combination(state = {}, action)
,它也是一個reducer,能夠被再次和其餘reducer combine。通常combination等同於currentReducer,它的返回結果會賦給state,combination的關鍵代碼以下:
var finalReducerKeys = Object.keys(finalReducers) var hasChanged = false var nextState = {} for (var i = 0; i < finalReducerKeys.length; i++) { var key = finalReducerKeys[i] var reducer = finalReducers[key] var previousStateForKey = state[key] var nextStateForKey = reducer(previousStateForKey, action) nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey !== previousStateForKey } return hasChanged ? nextState : state
中間省略了reducer返回結果的校驗。reducer就是經過這種方式從state中拿到對應的state,而後把返回的數據組裝到state的對應位置,很巧妙!
它接收兩個參數actionCreators和dispatch。若是actionCreators是函數,就直接返回:
(...args) => dispatch(actionCreator(...args))
直接給這個返回的函數傳actionCreator的參數就能夠直接觸發dispatch,這也是bindActionCreators的目的,簡化操做,弱化dispatch的存在感。
若是actionCreators是個對象會進行另外的操做,返回一個對象,下面是關鍵代碼。
var keys = Object.keys(actionCreators) var boundActionCreators = {} for (var i = 0; i < keys.length; i++) { var key = keys[i] var actionCreator = actionCreators[key] boundActionCreators[key] = (...args) => dispatch(actionCreator(...args)) } return boundActionCreators
其實裏面的處理和單個function的處理是同樣的。
一個能夠被執行三次的柯里化函數,代碼雖然簡單,可是給Redux帶來倒是無限可能。它的第一次執行的時候參數是一系列的middlewares,第二次執行參數是createStore,返回的是一個和createStore接收一樣參數的函數,再次被執行的話,就會返回dispatch強化以後的store。關鍵代碼以下:
var middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) } chain = middlewares.map(middleware => middleware(middlewareAPI)) const last = chain[funcs.length - 1] const rest = chain.slice(0, -1) dispatch = rest.reduceRight((composed, f) => f(composed), last(store.dispatch))
這裏只寫了多個middleware的狀況,單個middlewares更簡單。代碼很簡單,邏輯也很簡單,做用很大。好比使用thunk能夠作異步action。
以上基本是Redux的所有關鍵代碼剖析,Redux簡單而強大。