redux 的 middleware 是为了增强 dispatch 而出现的
设计和实现
// 这是一个简单的打日志中间件
function logger({ getState, dispatch }) {
// next 代表下一个中间件包装过后的 dispatch 方法,action 表示当前接收到的动作
return next => action => {
console.log("before change", action);
// 调用下一个中间件包装的 dispatch
let val = next(action);
console.log("after change", getState(), val);
return val;
};
}
// 使用 logger 中间件,创建一个增强的 store
let createStoreWithMiddleware = Redux.applyMiddleware(logger)(Redux.createStore);
function compose(...funcs: Function[]) {
return funcs.reduce((a, b) => (...args: any) => a(b(...args)));
}
function applyMiddleware(...middlewares) {
// middlewares 为中间件列表,返回一个接受原始 createStore 方法(Redux.createStore)作为参数的函数
return createStore => (...args) => {
// 创建原始的 store
const store = createStore(...args)
// 每个中间件都会被传入 middlewareAPI 对象,作为中间件参数
const middlewareAPI = {
getState: store.getState,
// 在这里 dispatch 使用匿名函数是为了能在 middleware 中调用 compose 的最新的 dispatch(闭包)
dispatch: (...args) => dispatch(...args)
}
// 给每个中间件传入 middlewareAPI 参数
// 中间件的统一模板为 next => action => next(action) 格式
// chain = [(next)=>(action)=>{…}, (next)=>(action)=>{…}, (next)=>(action)=>{…}],
// … 里闭包引用着 dispatch 和 getState
// 组成一个“洋葱式”的大函数,chain的每个函数元素相当于一层洋葱表皮
const chain = middlewares.map(middleware => middleware(middlewareAPI))
// 传入最原始 store.dispatch 方法,作为 compose 二级参数,compose 方法最终返回一个增强的dispatch 方法
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch // 返回一个增强版的 dispatch
}
}
}
闭包的使用
const middlewareAPI = {
getState: store.getState,
// 这里使用匿名函数就是为了捕获下面的 1 处已经增强的dispatch
dispatch: (...args) => dispatch(...args)
}
// 1
dispatch = compose(...chain)(store.dispatch);
简化的例子:
const middleware = ({dispatch}) => (next) => (number) => {
console.log("in middleware");
if(number !== 0){
return dispatch(--number);
}
return next(number);
}
function test() {
var dispatch = (number) => {
console.log("original dispatch");
return number;
};
var middlewareAPI = {
dispatch: (number) => {dispatch(number);}
}
dispatch = middleware(middlewareAPI)(dispatch);
return {
dispatch
}
}
var {dispatch} = test();
dispatch(3);
//输出
// "in middleware"
// "in middleware"
// "in middleware"
// "in middleware"
// "original dispatch"
//var middlewareAPI = {
// dispatch
// }
//输出:
// "in middleware"
// "original dispatch"