精益 React 學習指南 (Lean React)- 3.3 理解 redux 中間件

書籍完整目錄es6

3.3 理解 Redux 中間件

圖片描述

這一小節會講解 redux 中間件的原理,爲下一節講解 redux 異步 action 作鋪墊,主要內容爲:express

  • Redux 中間件是什麼redux

  • 使用 Redux 中間件segmentfault

  • logger 中間件結構分析後端

  • applyMiddleware數組

  • 中間件的執行過程閉包

3.3.1 Redux 中間件是什麼

Redux moddleware provides a third-party extension point between dispatching an action, and the moment it reaches the reducer.app

redux 提供了相似後端 Express 的中間件概念,本質的目的是提供第三方插件的模式,自定義攔截
action -> reducer 的過程。變爲 action -> middlewares -> reducer 。這種機制能夠讓咱們改變數據流,實現如異步 action ,action 過濾,日誌輸出,異常報告等功能。異步

3.3.2 使用 Redux 中間件

Redux 提供了一個叫 applyMiddleware 的方法,能夠應用多箇中間件,以日誌輸出中間件爲例async

import { createStore, applyMiddleware } from 'redux'
import createLogger from 'redux-logger'
import rootReducer from './reducers'

const loggerMiddleware = createLogger()
const initialState = {}

return createStore(
    rootReducer,
    initialState,
    applyMiddleware(
      loggerMiddleware
    )
  )

3.3.3 logger 中間件結構分析

看看 redux-logger 的源碼結構

function createLogger(options = {}) {
  /**
   * 傳入 applyMiddleWare 的函數
   * @param  {Function} { getState      }) [description]
   * @return {[type]}      [description]
   */
  return ({ getState }) => (next) => (action) => {
    let returnedValue;
    const logEntry = {};
    logEntry.prevState = stateTransformer(getState());
    logEntry.action = action;
    // .... 
    returnedValue = next(action);
    // ....
    logEntry.nextState = stateTransformer(getState());
    // ....
    return returnedValue;
  };
}

export default createLogger;

Logger 中這樣的結構 ({ getState }) => (next) => (action) => {} 看起來是很奇怪的,這種設計若是沒有 es6 的箭頭函數,擴展下來就是

/**
 * getState 能夠返回最新的應用 store 數據
 */
function ({getState}) {
    /**
     * next 表示執行後續的中間件,中間件有可能有多個
     */
    return function (next) {
        /**
         * 中間件處理函數,參數爲當前執行的 action 
         */
        return function (action) {...}
    }
}

這樣的結構本質上就是爲了將 middleware 串聯起來執行,爲了分析 middleware 的執行順序,還得看看 applyMiddleware 的實現

3.3.4 applyMiddleware 分析

下面是 applyMiddleware 完整的代碼,參數爲 middlewares 數組:

import compose from './compose'

/**
 * Creates a store enhancer that applies middleware to the dispatch method
 * of the Redux store. This is handy for a variety of tasks, such as expressing
 * asynchronous actions in a concise manner, or logging every action payload.
 *
 * See `redux-thunk` package as an example of the Redux middleware.
 *
 * Because middleware is potentially asynchronous, this should be the first
 * store enhancer in the composition chain.
 *
 * Note that each middleware will be given the `dispatch` and `getState` functions
 * as named arguments.
 *
 * @param {...Function} middlewares The middleware chain to be applied.
 * @returns {Function} A store enhancer applying the middleware.
 */
export default function applyMiddleware(...middlewares) {
  return (createStore) => (reducer, preloadedState, enhancer) => {
    var store = createStore(reducer, preloadedState, enhancer)
    var dispatch = store.dispatch
    var chain = []

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

    return {
      ...store,
      dispatch
    }
  }
}
  1. applyMiddleware 執行事後返回一個閉包函數,目的是將建立 store的步驟放在這個閉包內執行,這樣 middleware 就能夠共享 store 對象。

  2. middlewares 數組 map 爲新的 middlewares 數組,包含了 middlewareAPI

  3. compose 方法將新的 middlewaresstore.dispatch 結合起來,生成一個新的 dispatch 方法

  4. 返回的 store 新增了一個 dispatch 方法, 這個新的 dispatch 方法是改裝過的 dispatch,也就是封裝了中間件的執行。

因此關鍵點來到了 compose 方法了,下面來看一下 compose 的設計:

export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  const last = funcs[funcs.length - 1]
  const rest = funcs.slice(0, -1)
  return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
}

能夠看到 compose 方法實際上就是利用了 Array.prototype.reduceRight 。若是對 reduceRight 不是很熟悉,來看看下面的一個例子就清晰了:

/**
 * [description]
 * @param  {[type]} previousValue [前一個項]
 * @param  {[type]} currentValue  [當前項]
 */
[0, 1, 2, 3, 4].reduceRight(function(previousValue, currentValue, index, array) {
  return previousValue + currentValue;
}, 10);

執行結果:

# previousValue currentValue return value
第一次 10 4 14
第二次 14 3 17
第三次 17 2 19
第四次 19 1 20
第五次 20 0 20

3.3.5 理解中間件的執行過程

經過上面的 applyMiddleware 和 中間件的結構,假設應用了以下的中間件: [A, B, C],一個 action 的完整執行流程

初始化階段

一箇中間件的結構爲

function ({getState}) {
    return function (next) {
        return function (action) {...}
    }
}

初始化階段一:middlewares map 爲新的 middlewares

chain = middlewares.map(middleware => middleware(middlewareAPI))

執行事後,middleware 變爲了

function (next) {
    return function (action) {...}
}

初始化階段二:compose 新的 dispatch

const newDispatch = compose(newMiddlewares)(store.dispatch)

dispatch 的實現爲 reduceRight, 當一個新的 action 來了事後

/**
 * 1. 初始值爲: lastMiddleware(store.dispatch)
 * 2. previousValue: composed
 * 3. currentValue: currentMiddleware
 * 4. return value: currentMiddleware(composed) => newComposed
 */
rest.reduceRight((composed, f) => f(composed), last(...args))

composed 流程

reduceRight 的執行過程:

初始時候

  1. initialValue: composedC = C(store.dispatch) = function C(action) {}

  2. next 閉包: store.dispatch

第一次執行:

  1. previousValue(composed): composedC

  2. currentValue(f): B

  3. return value: composedBC = B(composedC) = function B(action){}

  4. next 閉包 composedC

第二次執行:

  1. previousValue(composed): composedBC

  2. currentValue(f): A

  3. return value: composedABC = A(composedBC) = function A(action){}

  4. next 閉包 composedBC

最後的返回結果爲 composedABC

執行階段

  1. dispatch(action) 等於 composedABC(action) 等於執行 function A(action) {...}

  2. 在函數 A 中執行 next(action), 此時 A 中 nextcomposedBC,那麼等於執行 composedBC(action) 等於執行 function B(action){...}

  3. 在函數 B 中執行 next(action), 此時 B 中 nextcomposedC,那麼等於執行 composedC(action) 等於執行 function C(action){...}

  4. 在函數 C 中執行 next(action), 此時 C 中 nextstore.dispatch 即 store 原生的 dispatch, 等於執行 store.dispatch(action)

  5. store.dispatch 會執行 reducer 生成最新的 store 數據

  6. 全部的 next 執行完事後開始回溯

  7. 執行函數 C 中 next 後的代碼

  8. 執行函數 B 中 next 後的代碼

  9. 執行函數 A 中 next 後的代碼

整個執行 action 的過程爲 A -> B -> C -> dispatch -> C -> B -> A

相關文章
相關標籤/搜索