Redux中间件原理详解
为了解析中间件,先看一下几个中间件是什么样子,怎么用,运行起来的原理是什么?
1、中间件是什么样子的
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;
2、怎么使用中间件
const store = createStore(rootReducer, initialState, applyMiddleware(thunk), ... ... );
简单来说,createStore做了这么件事:
目的:根据你传入的reducer和初始状态initialState生成初始化store,并提供了一些列操作的接口,像dispatch等
怎么做的呢?参考Redux-creatStore/compose
本文重点讲解中间件的执行过程和原理
3、中间件运行原理
中间件的执行原理和koa中间件的执行原理类似,但是不是洋葱型的,而是半个洋葱,因为redux是单向执行的,走过去就完事了。
当你应用了中间件,在触发一个action操作的时候,action操作就会经过先经过中间件,最终再形成dispatch(action)。
以其中两个中间件为例,说明下,一个触发一个action动作的时候,代码的执行逻辑。
thunk:是允许dispatch一个函数,而不是一个对象
假如说异步打印一个日志。
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
没有中间件的时候是这样的
store = { dispatch, ... ..., subscribe, getState }
经过中间包装后,store变成了
store = { dispatch: a(b((store.dispatch))), ... ..., subscribe, getState }
总结起来就是给每一个中间件配发了一个原始store的dispatch,中间件函数嵌套执行
3.2 触发一个action时,执行逻辑
假设触发一个异步打印日志的功能
- 应用中间件
const store = createStore(rootReducer, initialState, applyMiddleware(thunk) );
经过上面的操作,现在的store应该是
{ dispatch: action => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return next(action); }, ... ..., subscribe, getState }
- action函数
当执行这个logNext的时候,返回一个函数,函数的参数是dispatch和getState两个。
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就是每一个中间件要做的事情
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))) }