Redux源碼剖析

前面寫了《React組件性能優化》《Redux性能優化》《React-Redux性能優化》,可是都沒有從這些框架的實現上講爲何?此次就從源碼上來分析一下這些框架的實現原理,以更深刻的理解這些框架,並更好的使用它們。redux

Redux的api很簡單,下面一個一個的分析。segmentfault

createStore

首先說下它的三個參數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

subscribe

在createStore內部維護了兩個數組currentListeners、nextListeners。nextListeners的存在是爲了不在listeners執行過程當中,listeners發生改變,致使錯誤。listeners的添加或刪除都是對nextListeners進行操做的。框架

nextListeners = currentListeners.slice()

保證nextListeners的修改不會影響currentListeners。異步

subscribe(listener)的返回值是個函數,執行這個函數就會unsubscribe(listener),取消監聽。函數

dispatch

很重要,由於只能經過它修改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。

getState

獲取store中的state,很簡單,主要代碼:

return currentState

在沒有dispatch正在執行的狀況下,直接返回前面說很重要的currentState。

replaceReducer

參數是nextReducer,也很簡單,關鍵代碼:

currentReducer = nextReducer
dispatch({ type: ActionTypes.INIT })

直接拿nextReducer替換掉前面說很重要的currentReducer,後面再執行dispatch,action就會被nextReducer處理,處理的結果賦值給currentState。替換以後會執行一遍初始化action。

combineReducers

代碼雖然看起來很長,可是大多都是用來處理校驗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的對應位置,很巧妙!

bindActionCreators

它接收兩個參數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的處理是同樣的。

applyMiddleware

一個能夠被執行三次的柯里化函數,代碼雖然簡單,可是給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簡單而強大。

相關文章
相關標籤/搜索