Redux-Middleware-原始碼解析

mazy發表於2018-06-25

image


一句話總結

Middleware就是增強了dispatch


開始呼叫createStore發生了什麼

//呼叫
const store = createStore(rootReducer, applyMiddleware(...middlewares));
//createStore
export default function createStore(reducer, preloadedState, enhancer)
  //如果第二個引數是function,並且沒傳第三個引數,則將第二個引數賦值給第三個引數,然後將第二個引數設為undefined
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }

  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }
    //返回一個高階函式
    return enhancer(createStore)(reducer, preloadedState)
  }
} 
複製程式碼

通過呼叫createStore 返回的結果可以解析為

applyMiddleware(...middlewares)(createStore)(reducer, initialState)
複製程式碼

applyMiddleware原始碼裡發生了什麼

//呼叫applyMiddleware,可以傳入多箇中介軟體
export default function applyMiddleware(...middlewares) {
  return (createStore) => (reducer, initialState, enhancer) => {
    var store = createStore(reducer, initialState, enhancer)

    var dispatch = store.dispatch
    var chain = []
    //將state和dispatch所指向的函式繫結到middlewareAPI
    var middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    }
    //迭代中介軟體陣列,並執行一遍,將middlewareAPI作為最外層的store,並返回一個相當於next函式的陣列
    chain = middlewares.map(middleware => middleware(middlewareAPI))

    //將陣列整理成巢狀的函式體,並將store.dispatch傳入最內側的函式的next,並返回經過處理的dispatch
    //dispatch是一個函式,是一個巢狀了多層的函式,其最裡面呼叫的是store.dispatch
    dispatch = compose(...chain)(store.dispatch)
    //返回一個新的store
    return {
      ...store,
      dispatch
    }
  }
}
複製程式碼

大致看下,其實就是通過一些操作,然後返回一個經過處理的store,dispatch

具體做了些什麼

var middlewareAPI = {
    getState: store.getState,
    dispatch: (action) => dispatch(action)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)

複製程式碼
  1. 定義了一個物件(middlewareAPI) ,並將 store.state和store.dispatch繫結到這個物件中(都是引用)
  2. 然後迴圈這些中介軟體,並將middlewareAPI作為引數執行middleware,因為都是高階函式,所以返回的是next陣列,並儲存到chain中
  3. 最重要的是下一步的compose函式
export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }
  if (funcs.length === 1) {
    return funcs[0]
  }
  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
複製程式碼

這裡有必要解釋下funcs.reduce((a, b) => (...args) => a(b(...args))) 這一坨做了些什麼?

reduce 是專門為累加操作設計的,啥意思呢

先把funcs.reduce((a, b) => (...args) => a(b(...args))) 翻譯一下

  1. 假如說 funcs[a,b,c,d,e,f]
  2. 那麼執行之後的結果就是a(b(c(d(e(f(...args)))
  3. 因為是compose(...chain)(store.dispatch)呼叫,所以...args就是store.dispatch,原生的dispatch,就是說最內層,呼叫的是原生的dispatch
  4. 這個有個洋蔥模型,網上覆制一個
    image

所以最後返回的dispatch是經過處理的dispatch:a(b(c(d(e(f(store.dispatch)))


解釋下chain是什麼?

  //以redux-saga為例
  //看引數,就知道為什麼定義middlewareAPI物件了
  function sagaMiddleware({ getState, dispatch }) {
    ...
    return next => action => {
      if (sagaMonitor && sagaMonitor.actionDispatched) {
        sagaMonitor.actionDispatched(action)
      }
      const result = next(action) // hit reducers
      channel.put(action)
      return result
    }
  }
複製程式碼
  1. 從原始碼上可以分析出當執行chain = middlewares.map(middleware => middleware(middlewareAPI))時 直接返回了next()函式
  2. 當有一堆middleware時,執行middleware都返回一個next()函式
  3. 所以chain就是一個next()陣列
  4. 而這個next()其實就是下一個middleware
  5. 一直next到最裡面的執行store.dispatch

流程

  1. 呼叫applyMiddleware傳入n個 middleware
  2. 用 middlewareAPI 儲存了當前的store.state,store.dispatch(每個middleware共享)
  3. 迭代middlewares,執行每個middleware並攜帶middlewareAPI,返回一個next()
  4. 將chain整理成巢狀的函式,最裡層呼叫store.dispatch
  5. 返回一個經過處理的dispatch

用一個最噁心的方式總結

//模擬三個middleware
function A(next){
    return function A1(action){
        next(action)
    }
}
function B(next){
    return function B1(action){
        next(action)
    }
}
function C(next){
    return function C1(action){
        next(action)
    }
}
複製程式碼

假設 dispatch = A(B(C(store.dispatch))),開始執行

function A1(action){
    function B1(action){
        return function C1(action){
            store.dispatch(action)
        }
    }
}(action)
複製程式碼

一個action的執行順序:A(action) -> B(action) -> C(action) -> store.dispatch(action),先從內到外生成新的func,然後由外向內執行.

相關文章