爲了解析中間件,先看一下幾個中間件是什麼樣子,怎麼用,運行起來的原理是什麼?redux
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;
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); }; }
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
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,中間件函數嵌套執行異步
假設觸發一個異步打印日誌的功能函數
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 }
當執行這個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 => {}
明白了麼?
// 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))) }