这个函数设计的很巧妙,值得学习:
export default function applyMiddleware(...middlewares) {
// 返回一个匿名函数,接收一个 createStore 参数来创建 store
return createStore => (...args) => {
// 这里的 args 就是 reduces ,创建一个store
const store = createStore(...args)
// 默认dispatch,防止你的中间件是初始化时调用
let dispatch = () => {
throw new Error(
'Dispatching while constructing your middleware is not allowed. ' +
'Other middleware would not be applied to this dispatch.'
)
}
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
// 中间件的初始化,每个中间件都传入 getState 和 dispatch
const chain = middlewares.map(middleware => middleware(middlewareAPI))
// 使用函数 compose,将所有的中间件串接起来(该函数上面有讲)。
// 也就是所有的中间件都能拿到 dispatch 函数
// 这里会生成一个新的 dispatch 函数,给 middlewareAPI用。
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
源码的代码量很少,但是却实现了所有的 middleware
可以嵌套调用。
其实看到这里还是有很多疑惑,它是怎么实现所有 middleware
嵌套调用的。
这里要结合一个标准的 middle
例子来说明。
来看redux-thunk
。这个库的代码也很少,是一个标准的 middle
。
redux-thunk 源码:
function createThunkMiddleware(extraArgument) {
return function ({dispatch, getState}) {
// {dispatch, getState} 就是 middlewareAPI
return function (next) {
// next 就是 store.dispatch,是通过 compose 传给每个中间件的
return function (action) {
// 这个函数就我们写 dispatch 调用到的函数,action就是我们的参数。
if (typeof action === 'function') {
// 如果 action 是个函数,就是我们所说的异步操作,把 dispatch, getState 传入
return action(dispatch, getState, extraArgument);
}
// 否则执行 dispatch 。例: dispatch({type: 'INIT', payload: 'test'})
return next(action);
};
};
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
redux 使用:
import thunkMiddleware from "redux-thunk";
import { applyMiddleware, combineReducers, createStore } from "redux";
const remoteActionMiddleware = applyMiddleware(thunkMiddleware)(createStore);
// Store
const stores = remoteActionMiddleware(
combineReducers({
...reducers
}),
);
😃😃😃 是不是有点眉目了,顿时代码都变得清秀了。。。
那action是怎样经过所有中间件的呢?
源码是这样的:
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
那 next
这个函数返回什么呢,我们知道它其实就是 store.dispatch
,这里就要看 createStore.js
里面的dispatch
了:
function dispatch(action) {
// 其他省略
// 返回当前的action,例: {type: 'INIT'} 或 是个异步函数
return action
}
嗯,没错store.dispatch
返回的就是action
。再加上中间件的嵌套,每个中间件都返回 action
,然后又可以供下一个next
使用。就这样形成了一个完整的链条。
总结
使用
compose
来实现中间件的嵌套调用。 返回一个全新的函数,可以接受新的参数(上一个中间件处理过的 dispatch),最终返回一个全新的包含新逻辑的 dispatch 方法。next => action => { // 这里的next就是dispatch return next(action); }
将中间件初始化,每个中间件都传入 getState 和 dispatch,并且返回一个新的函数,该函数接收
next
作为参数(也就是dispatch
)。再返回一下新的dispatch
,供下一个middle
使用。这样就形成了嵌套调用。