Redux中間件原理詳解

爲了解析中間件,先看一下幾個中間件是什麼樣子,怎麼用,運行起來的原理是什麼?redux

一、中間件是什麼樣子的

1.2 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;

1.2promise中間件

import isPromise from 'is-promise';
import { isFSA } from 'flux-standard-action';

export default function promiseMiddleware({ dispatch }) {
  return next => action => {
    if (!isFSA(action)) {
      return isPromise(action) ? action.then(dispatch) : next(action);
    }

    return isPromise(action.payload)
      ? action.payload
          .then(result => dispatch({ ...action, payload: result }))
          .catch(error => {
            dispatch({ ...action, payload: error, error: true });
            return Promise.reject(error);
          })
      : next(action);
  };
}

1.3logger中間件

const defaultLogger = ({ dispatch, getState } = {}) => {
  if (typeof dispatch === 'function' || typeof getState === 'function') {
    return createLogger()({ dispatch, getState });
  }
};
function createLogger(options = {}) {
  const loggerOptions = Object.assign({}, defaults, options);

  const {
    logger,
    stateTransformer,
    errorTransformer,
    predicate,
    logErrors,
    diffPredicate,
  } = loggerOptions;

  if (typeof logger === 'undefined') {
    return () => next => action => next(action);
  }

  import { logger } from 'redux-logger'
  const store = createStore(
      reducer,
      applyMiddleware(logger))
    
  import { createLogger } from 'redux-logger'
  const logger = createLogger({
      // ...options
    });
  const store = createStore(
      reducer,
      applyMiddleware(logger));
      
  return () => next => action => next(action);
  }

  const logBuffer = [];

  return ({ getState }) => next => (action) => {
    if (typeof predicate === 'function' && !predicate(getState, action)) {
      return next(action);
    }
    const logEntry = {};

    logBuffer.push(logEntry);

    logEntry.started = timer.now();
    logEntry.startedTime = new Date();
    logEntry.prevState = stateTransformer(getState());
    logEntry.action = action;

    let returnedValue;
    if (logErrors) {
      try {
        returnedValue = next(action);
      } catch (e) {
        logEntry.error = errorTransformer(e);
      }
    } else {
      returnedValue = next(action);
    }

    logEntry.took = timer.now() - logEntry.started;
    logEntry.nextState = stateTransformer(getState());

    const diff = loggerOptions.diff && typeof diffPredicate === 'function'
      ? diffPredicate(getState, action)
      : loggerOptions.diff;

    printBuffer(logBuffer, Object.assign({}, loggerOptions, { diff }));
    logBuffer.length = 0;

    if (logEntry.error) throw logEntry.error;
    return returnedValue;
  };
}
export { defaults, createLogger, defaultLogger as logger };

export default defaultLogger;

二、怎麼使用中間件

const store = createStore(rootReducer, initialState, 
    applyMiddleware(thunk),
    ... ...
);

簡單來講,createStore作了這麼件事:
目的:根據你傳入的reducer和初始狀態initialState生成初始化store,並提供了一些列操做的接口,像dispatch等
怎麼作的呢?參考Redux-creatStore/compose
本文重點講解中間件的執行過程和原理數組

三、中間件運行原理

中間件的執行原理和koa中間件的執行原理相似,可是不是洋蔥型的,而是半個洋蔥,由於redux是單向執行的,走過去就完事了。
當你應用了中間件,在觸發一個action操做的時候,action操做就會通過先通過中間件,最終再造成dispatch(action)。
以其中兩個中間件爲例,說明下,一個觸發一個action動做的時候,代碼的執行邏輯。
thunk:是容許dispatch一個函數,而不是一個對象
假如說異步打印一個日誌。promise

3.1 中間件的內部邏輯

const store = createStore(reducer, preloadedState, enchancer);

// 若是沒有中間件,正常觸發一個action;
// 若是有中間件的時候 creatStore內部的執行邏輯是這樣的
// enchancer 就是你應用的中間件,調用applyMiddleware獲得的組合中間件

function applyMiddleware(...middlewares) {
  return createStore => (...args) => {
  
    // 該建立的store仍是要建立的,只傳入了兩個參數,沒有中間件,獲得的是正常的store
    const store = createStore(...args)
    let dispatch = () => {
      throw new Error(
        `Dispatching while constructing your middleware is not allowed. ` +
          `Other middleware would not be applied to this dispatch.`
      )
    }
    // 把getState、dispatch傳給中間件
    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    }
    // 下面方法返回來了一個函數數組,中間件被剝離到
    // next => {}
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    // 再執行下面的,中間件就被剝離到
    // action => {}
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

// 下面獲得的結果是 
// 假設中間件爲 a b
// a(b(store.dispatch))
return enchancer(createStore)(reducer, preloadedState);

// 結合上面thunk的源碼
({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
        return action(dispatch, getState);
    }
    return next(action);
  };

通過上面的操做後,通過中間件包裝後的store是什麼樣子
假設中間件爲 a b c
沒有中間件的時候是這樣的app

store = {
    dispatch,
    ... ...,
    subscribe,
    getState
}

通過中間包裝後,store變成了koa

store = {
    dispatch: a(b((store.dispatch))),
    ... ...,
    subscribe,
    getState
}

總結起來就是給每個中間件配發了一個原始store的dispatch,中間件函數嵌套執行異步

3.2 觸發一個action時,執行邏輯

假設觸發一個異步打印日誌的功能函數

  • 應用中間件
const store = createStore(rootReducer, initialState, 
    applyMiddleware(thunk)
);

通過上面的操做,如今的store應該是this

{
    dispatch: action => {
        if (typeof action === 'function') {
            return action(dispatch, getState, extraArgument);
        }
        return next(action);
    },
    ... ...,
    subscribe,
    getState
}
  • action函數

當執行這個logNext的時候,返回一個函數,函數的參數是dispatch和getState兩個。spa

const logNext = () => (dispatch, getState) => {
    setTimeout(
        dispatch({
            type: 'LOG',
            payload: {
                content: '這是一條異步日誌'
            }})
        ,5000);
}
  • 執行過程
---->
store.dispatch(logNext())  // 傳了一個函數,而後執行一個函數
----> 
(dispatch, getState) => {
    setTimeout(
        dispatch({
            type: 'LOG',
            payload: {
                content: '這是一條異步日誌'
            }})
        ,5000);
} 
---->

能夠看出來,redux-thunk就是一個封裝函數,容許store.dispatch一個函數
若是有多箇中間件,執行過程是什麼樣子的?重點在next(action),next是什麼呢?
next就是每個中間件要作的事情.net

next => action => {}

明白了麼?

附錄

compsoe

// compose自己並不改變函數的執行,將函數組合後又返回了一個函數
    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)))
    }
相關文章
相關標籤/搜索