如果你用过express.js之类的web框架,对中间件(Middleware)这个概念可能不会陌生。中间件其实就是一种独立运行于各个框架组件之间的胶水代码。在Express.js或Koa等框架中,中间件通常是运行在收到请求到处理请求之间,可是实现日志记录、身份认证等预处理操作。而在Redux里,中间件是运行在action发送出去,到达reducer之间的一段代码。
编写中间件日志记录是开发过程中常用的一个功能,你可以选择侵入业务逻辑来记录日志(不推荐),也可以选择使用中间件来实现这个功能。接下来让我们编写一个常用的日志记录中间件:
const loggerMiddleware = store => next => action => {
console.group(action.type);
console.log('action: ', action);
const result = next(action);
console.log('next state: ', store.getState());
console.groupEnd(action.type);
return result;
}
使用中间件的时候,要在初始化store的时候利用applyMiddleware
注入进去:
const store = createStore(
rootReducer,
applyMiddleware(loggerMiddleware)
)
这样,我们在每次触发action的时候就能记录我们所需要的信息。同样的方式,也可以实现日志上报等功能。
组合中间件中间件其实是一种高层次的抽象,可以将核心领域业务和基础架构逻辑解耦开来。而多个中间件可以组合使用,从而使每一个中间件能够保持“小而美”的特性。
const store = createStore(
rootReducer,
applyMiddleware(thunk, loggerMiddleware)
)
其中applyMiddleware
函数可以接收多个中间件,源码如下:
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
}
}
}
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))
}
我们可以看到它主要做了几个工作:
- 返回一个高阶函数,这个函数中会初始化store和重写dispatch逻辑,以便后续使用。
- 将
store.getState
,dispatch
传入每个中间件中,并收集调用链结果。
之后在应用中所有使用的dispatch都将是修改过的逻辑,从中我们可以看出有点面向切面编程
的味道,可以好好体会一下。