淺談redux 中間件的原理

在使用redux管理異步數據流的時候,咱們會使用中間件,以redux-thunk中間件爲例,咱們作一下分析:ajax

首先是構建store,咱們須要如下代碼進行揉入中間件的相似creatStore函數的構造:json

const loggerMiddleware = createLogger();

const createStoreWithMiddleware = applyMiddleware(
  thunkMiddleware,
  loggerMiddleware
)(createStore);

export default function configureStore(initialState) {
    return createStoreWithMiddleware(rootReducer, initialState);
}

在這段代碼中,咱們用到了 redux

applyMiddleware 函數去將中間件揉入構造store的工廠函數中,
applyMiddleware函數的源碼以下所示:
import compose from './compose'
export default function applyMiddleware(...middlewares) {
  return (createStore) => (reducer, initialState, enhancer) => {
    var store = createStore(reducer, initialState, 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
    }
  }
}
先看函數的return,咱們經過applyMiddleware構建獲得的createStoreWithMiddleware函數實際上是這樣一個函數

function (reducer, initialState, enhancer){
      .......
      return {
          ...store,
          dispatch
     }

}

而store就是它return出來的這個對象數組

 

store的創建流程大體就是這樣,但此時store的dispatch方法已經不是原來的dispatch,注意下面的代碼:數據結構

chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)

這纔是 applyMiddleware 方法的核心,它將每一個middleware進行處理,並存入到chain 的數組中,而後調用compose方法對chain數組進行處理,處理出來的返回值就是store的dispatch,也就是咱們在業務代碼中用到的dispatchapp

接下來看一下compose方法作了什麼:異步

export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  } else {
    const last = funcs[funcs.length - 1]
    const rest = funcs.slice(0, -1)
    return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
  }
}

它經過調用數組的reduceRight方法對各個中間件進行整合函數

reduceRight方法的 第一參數是 callback(preValue,curValue) ,第二個參數是要傳遞給callback做爲第一個參數preValue來使用的fetch

這時咱們回到applyMiddleware方法中的這段代碼:jsonp

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

dispatch = compose(...chain)(store.dispatch)

由此咱們能夠知道傳給last(...args)的正是 store.dispatch

咱們根據官方文檔能夠知道中間件的通用構造以下:(箭頭函數這裏略過)

function middleware({dispatch, getState}) {
    return function (next) {
        return function (action) {
            return next(action);
        }
    }
}

chain數組裏面的數據結構是這樣的 :[ function(next){return function(action){...}},function(next){return function(action){...}} ...]

也就是 middleware函數的裏面一層 的這個函數

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

而後再通過compose的進一步reduceRight提煉:

return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))

compose就是 function(action){return next(action)}
在通過 f(composed) 還是   function(action){return next(action);} 只不過這裏的next是上一個中間件的返回值,追溯源頭,next其實就是 store.dispatch一路通過f(composed)這種方式將中間件的方法揉和進來的變種dispatch

因此通過揉入中間件的createStore工廠函數返回的store對象的dispatch方法,其實就是function(action){return next(action);}

而後結合實際中咱們使用redux-thunk進行異步數據操做,thunk源碼以下:

function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }
    return next(action);
  };
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;


//箭頭函數轉變正常就是這樣

function createThunkMiddleware(extraArgument) {
  return function({ dispatch, getState }){
       return  function(next){
             return function(action){
               if (typeof action === 'function') {
                 return action(dispatch, getState, extraArgument);
               }
               return next(action);
            }
     }
  };
}

經過上面分析,咱們的store.dispatch就是這個東西

           function(action){
               if (typeof action === 'function') {
                 return action(dispatch, getState, extraArgument);
               }
               return next(action);
            }

咱們在調用異步獲取數據的時候 action是這樣寫的:

export function fetchUri(key){
    return function(dispatch){
        dispatch(request("show"));
        return $.ajax({
            url:BOOKLIST_REQ.uri,
            dataType:"jsonp",
            data:{q:key,count:BOOKLIST_REQ.count}
        }).done(res=>{
            dispatch(receive(res));
            dispatch(request("hidden"));
        }).fail(res=>console.error(res));
    };
}

激發調取異步數據方法是  store.dispatch(fetchUrl("xxx"));

這樣後面的就不用細說了,怎麼執行下來就一目瞭然了吧

這就是整個中間件大體的工做過程,若是有什麼說的不對的地方,你特麼來打我呀!

相關文章
相關標籤/搜索