前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Redux(七):源码分析之applyMiddleware

Redux(七):源码分析之applyMiddleware

原创
作者头像
Ashen
修改2020-06-01 14:38:06
5450
修改2020-06-01 14:38:06
举报

一、源码分析

Redux真正强大之处便是中间件系统,利用各类自定义或第三方的中间件,可以极大丰富其功能,派发的action也将不再局限于普通纯对象。

还记得createStore里enhancer参数吗?在createStore.js的53行,如果传递了enhancer,则:

return enhancer(createStore)(reducer, preloadedState)

接着看下边的实例:

const initialState = {
  todos:[]
};
const reducer = function(state,action){
  switch (action.type){
    case "TODO_ADD":
      return Object.assign({},state,{
        todos:state.todos.concat({text:action.text})
      })
  }
  return state;
};
const enhancer = function(createStore){
  return function(reducer, preloadedState){
    return createStore(reducer,preloadedState);
  }
};
const store = createStore(reducer,initialState,applyMiddleware(enhancer));

可以看出来,这里store是执行enhancer的返回函数所创建的。其实enhancer就是applyMiddleware的返回结果,我们来看看applyMiddleware的源码:

export default function applyMiddleware(...middlewares) {
  return createStore => (...args) => {
    const store = createStore(...args)
    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)
    }
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

这个看起来不够直观,转成ES5,那么就是这样的:

function applyMiddleware(...middlewares){
  return function(createStore){
    return function(reducer,preloadedState){
      const store = createStore(reducer,preloadedState);
      let dispatch = function(){
        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:function(...args){
          return dispatch(...args);
        }
      };
      const chain = middlewares.map(function(middleware){
        return middleware(middlewareAPI);
      });
      dispatch = compose(...chain)(store.dispatch);
      return {
        ...store,
        dispatch
      }
    }
  }
}

对比我们的enhancer函数,是不是很像呢,接着逐行分析最里面的代码。

first step:

const store = createStore(reducer,preloadedState);
let dispatch = function(){
  throw new Error(
    `Dispatching while constructing your middleware is not allowed. ` +
    `Other middleware would not be applied to this dispatch.`
  )
};

创建store,自定义dispatch错误函数,这个函数的意思:不能在中间件里面直接调用根dispatch,应该调用形参dispatch。

second step:

const middlewareAPI = {
  getState: store.getState,
  dispatch:function(...args){
    return dispatch(...args);
  }
};
const chain = middlewares.map(function(middleware){
  return middleware(middlewareAPI);
});

定义middlewareAPI对象,包含getState和dispatch俩个属性。

定义chain数组,每个元素是middleware的执行结果(依然是函数),参数就是middlewareAPI。在中间件里面不能直接调用middlewareAPI的dispatch。

third step:

dispatch = compose(...chain)(store.dispatch);
return {
  ...store,
  dispatch
}

结合上一章compose的解读,那么中间件的执行顺序就是由后向前的,执行队列的每个dispatch形参都由上个中间件的执行结果返回。

二、自定义中间件格式

结合上边的分析,那么一个最最简单的中间件应该是这种格式的。

function middleware(middlewareAPI){
  return function(next){
    return function(action){
      next(action);
    }
  }
}

我们定义俩个中间件:

function middleware1(middlewareAPI){
  return function ware1(next){
    return function(action){
      console.log("middleware1开始");
      next(action);
      console.log("middleware1结束");
    }
  }
}
function middleware2(middlewareAPI){
  return function ware2(next){
    return function(action){
      console.log("middleware2开始");
      next(action);
      console.log("middleware2结束");
    }
  }
}

结合上边的源码,那么在applyMiddlewares中数组常量chain的格式就是这样的:

const chain = [
  function ware1(next){
    return function(action){
      console.log("middleware1开始");
      next(action);
      console.log("middleware1结束")
    }
  },
  function ware2(dispatch){
    return function(next){
      console.log("middleware2开始");
      next(action);
      console.log("middleware2结束")
    }
  },
];

接着经过compose函数的运算,所以原始的dispatch会作为ware2中间件的参数,然后ware2的返回值会作为ware1的参数,那么最终dispatch函数就是这样子的:

const dispatch = function(action){
  console.log("middleware1开始");
  void function(next){
    console.log("middleware2结束");
    next(action);
    console.log("middleware2结束")
  }(next)
  console.log("middleware1结束")
};

那么当派发一个action之后,正确的执行顺序就是:

  1. 打印middleware1开始
  2. 打印middleware2开始
  3. 执行原始的dispatch函数
  4. 执行subscribe注册的回调函数
  5. 打印middleware2结束
  6. 打印middleware1结束

总结

综上,调用next之前可以获取到当前state树,调用结束后可以获取到更新后的state树,基本中间件格式:

function middleware(middlewareAPI){
  return function(next){
    return function(action){
      console.log("middleware开始",middlewareAPI.getState());
      next(action);
      console.log("middleware结束",middlewareAPI.getState());
    }
  }
}

同时中间件的引用顺序也是有讲究的,第一个中间的action回传给redux必须是纯对象。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、源码分析
    • first step:
      • second step:
        • third step:
        • 二、自定义中间件格式
        • 总结
        相关产品与服务
        消息队列 TDMQ
        消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档