书籍完整目录es6
这一小节会讲解 redux 中间件的原理,为下一节讲解 redux 异步 action 作铺垫,主要内容为:express
Redux 中间件是什么redux
使用 Redux 中间件segmentfault
logger 中间件结构分析后端
applyMiddleware数组
中间件的执行过程闭包
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 过滤,日志输出,异常报告等功能。异步
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 ) )
看看 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 的实现
下面是 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 } } }
applyMiddleware
执行事后返回一个闭包函数,目的是将建立 store
的步骤放在这个闭包内执行,这样 middleware
就能够共享 store
对象。
middlewares
数组 map 为新的 middlewares
数组,包含了 middlewareAPI
compose
方法将新的 middlewares
和 store.dispatch
结合起来,生成一个新的 dispatch
方法
返回的 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 |
经过上面的 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))
reduceRight 的执行过程:
初始时候
initialValue: composedC = C(store.dispatch) = function C(action) {}
next 闭包: store.dispatch
第一次执行:
previousValue(composed): composedC
currentValue(f): B
return value: composedBC = B(composedC) = function B(action){}
next 闭包 composedC
第二次执行:
previousValue(composed): composedBC
currentValue(f): A
return value: composedABC = A(composedBC) = function A(action){}
next 闭包 composedBC
最后的返回结果为 composedABC
dispatch(action)
等于 composedABC(action)
等于执行 function A(action) {...}
在函数 A 中执行 next(action)
, 此时 A 中 next
为 composedBC
,那么等于执行 composedBC(action)
等于执行 function B(action){...}
在函数 B 中执行 next(action)
, 此时 B 中 next
为 composedC
,那么等于执行 composedC(action)
等于执行 function C(action){...}
在函数 C 中执行 next(action)
, 此时 C 中 next
为 store.dispatch
即 store 原生的 dispatch, 等于执行 store.dispatch(action)
store.dispatch 会执行 reducer 生成最新的 store 数据
全部的 next 执行完事后开始回溯
执行函数 C 中 next 后的代码
执行函数 B 中 next 后的代码
执行函数 A 中 next 后的代码
整个执行 action 的过程为 A -> B -> C -> dispatch -> C -> B -> A