这个函数设计的很巧妙,值得学习:

  1. export default function applyMiddleware(...middlewares) {
  2. // 返回一个匿名函数,接收一个 createStore 参数来创建 store
  3. return createStore => (...args) => {
  4. // 这里的 args 就是 reduces ,创建一个store
  5. const store = createStore(...args)
  6. // 默认dispatch,防止你的中间件是初始化时调用
  7. let dispatch = () => {
  8. throw new Error(
  9. 'Dispatching while constructing your middleware is not allowed. ' +
  10. 'Other middleware would not be applied to this dispatch.'
  11. )
  12. }
  13. const middlewareAPI = {
  14. getState: store.getState,
  15. dispatch: (...args) => dispatch(...args)
  16. }
  17. // 中间件的初始化,每个中间件都传入 getState 和 dispatch
  18. const chain = middlewares.map(middleware => middleware(middlewareAPI))
  19. // 使用函数 compose,将所有的中间件串接起来(该函数上面有讲)。
  20. // 也就是所有的中间件都能拿到 dispatch 函数
  21. // 这里会生成一个新的 dispatch 函数,给 middlewareAPI用。
  22. dispatch = compose(...chain)(store.dispatch)
  23. return {
  24. ...store,
  25. dispatch
  26. }
  27. }
  28. }

源码的代码量很少,但是却实现了所有的 middleware 可以嵌套调用。

其实看到这里还是有很多疑惑,它是怎么实现所有 middleware 嵌套调用的。
这里要结合一个标准的 middle 例子来说明。
来看redux-thunk。这个库的代码也很少,是一个标准的 middle

redux-thunk 源码:

  1. function createThunkMiddleware(extraArgument) {
  2. return function ({dispatch, getState}) {
  3. // {dispatch, getState} 就是 middlewareAPI
  4. return function (next) {
  5. // next 就是 store.dispatch,是通过 compose 传给每个中间件的
  6. return function (action) {
  7. // 这个函数就我们写 dispatch 调用到的函数,action就是我们的参数。
  8. if (typeof action === 'function') {
  9. // 如果 action 是个函数,就是我们所说的异步操作,把 dispatch, getState 传入
  10. return action(dispatch, getState, extraArgument);
  11. }
  12. // 否则执行 dispatch 。例: dispatch({type: 'INIT', payload: 'test'})
  13. return next(action);
  14. };
  15. };
  16. };
  17. }
  18. const thunk = createThunkMiddleware();
  19. thunk.withExtraArgument = createThunkMiddleware;
  20. export default thunk;

redux 使用:

  1. import thunkMiddleware from "redux-thunk";
  2. import { applyMiddleware, combineReducers, createStore } from "redux";
  3. const remoteActionMiddleware = applyMiddleware(thunkMiddleware)(createStore);
  4. // Store
  5. const stores = remoteActionMiddleware(
  6. combineReducers({
  7. ...reducers
  8. }),
  9. );

😃😃😃 是不是有点眉目了,顿时代码都变得清秀了。。。

那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使用。就这样形成了一个完整的链条。

总结

  1. 使用 compose来实现中间件的嵌套调用。 返回一个全新的函数,可以接受新的参数(上一个中间件处理过的 dispatch),最终返回一个全新的包含新逻辑的 dispatch 方法。

    next => action => { // 这里的next就是dispatch
    return next(action);
    }
    
  2. 将中间件初始化,每个中间件都传入 getState 和 dispatch,并且返回一个新的函数,该函数接收next作为参数(也就是dispatch)。再返回一下新的dispatch,供下一个middle使用。这样就形成了嵌套调用。