Redux中间件源码理解

由于近期在了解RN的框架,学习了Redux框架,在不了解中间件的前提下,Redux还是很好理解的。但是由于仍然对ES6的语法掌握的不熟练,导致对中间件的理解很慢,特此整理记录一下。

1、先看一下中间件的使用

//引用中间件
import {createStore,combineReducers,applyMiddleware} from 'redux';
import {createLogger} from 'redux-logger';
//创建一个store出来
this.reduxStore = createStore(reducer,applyMiddleware(logger));

OK,代码很简单,这里我们开始准备入手源码,来看一下中间件到底是如何起作用的。

2、store的创建

export default function createStore(reducer, preloadedState, enhancer) {
//这里判断了一下是否为两个参数的情况,就是上面代码给出的样例,如果在初始化state的哪里是一个方法,同时enhancer没定义,那么为两个参数的情况,于是将初始化state和enhancer参数进行互换
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }
//这里校验了一下enhancer必须是个function,否则就扔出去
  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }
//OK,重点来了,这里实际上调用的是applyMiddleware(logger)(createStore)(reducer, preloadedState);
    return enhancer(createStore)(reducer, preloadedState)
    ......//后面代码就不看了
  }

OK,上面的代码比较简单,就是对createStore进行了参数检测,因为这个构造可能为两个参数,也可能为三个参数,这里进行了一次参数的校准。然后进行了关键代码的调用enhancer(createStore)(reducer, preloadedState),这里enhancer实际上就是applyMiddleware(logger),那么我们再看一下applyMiddleware到底做了什么

3、核心代码applyMiddleware
这里的源码,大家可以去node_modules/redux/src/applyMiddleware.js中进行查看

export default function applyMiddleware(...middlewares) {
//这里实际上就是一个组合函数的调用f(g(k(x))),其实就是下个代码片段所显示的东西
  return (createStore) => (reducer, preloadedState, enhancer) => {
  //生成一个store
    var store = createStore(reducer, preloadedState, enhancer)
    //获取该store的dispatch方法
    var dispatch = store.dispatch
    //准备一个存放dispatch方法的方法链(实际上就是个数组)
    var chain = []
    //这个是middleware需要的参数,就是个store
    var middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    }
    //核心代码1:对所有的中间件进行一次转换,转换成一组dispatch
    chain = middlewares.map(middleware => middleware(middlewareAPI))
    //核心代码2:对转换成的一组dispatch进行重组,组合成一个方法(PS:由于对ES6语法不熟,这里浪费了好长时间)
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}
export default function applyMiddleware(...middlewares) {
  return function (createStore) {
   return function (reducer, preloadedState, enhancer) {
    var store = createStore(reducer, preloadedState, enhancer)
    ......
    return {...store,dispatch};
  }
}
//所以才可以这样调用applyMiddleware(middlewares)(createStore)(reducer, preloadedState)

代码异常的简洁。。。。。。。。。(PS:吐槽一下,菜鸡表示刚开始根本看不懂,这个中间件整整理解了一天才终于理解了点皮毛…….)

OK,接下来我们一行一行代码仔细分析:

//这三行代码不赘述,就是生成了一个store,然后拿到了这个store的dispatch方法,然后准备了一个dispatch链(就是个数组,先不用管里面放了什么东西)
var store = createStore(reducer, preloadedState, enhancer)
    var dispatch = store.dispatch
    var chain = []

这里先看一下middleware(middlewareAPI)这个方法

export interface Middleware {
  <S>(api: MiddlewareAPI<S>): (next: Dispatch<S>) => Dispatch<S>;
}

这个是在redux源码中找到的声明,可以看到,middleware接收一个middlewareAPI这样一个参数,返回一个入参为dispatch,返回值为dispatch的一个function,解释完这个之后看下面一个代码块

var middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    }
    chain = middlewares.map(middleware => middleware(middlewareAPI))

这里对所有的中间件进行了一次map操作,操作之后,chain数组中,放置了中间件所对应的入参为dispatch,返回值为dispatch的function。OK,继续看下面一个代码块。

dispatch = compose(...chain)(store.dispatch)//这是又一行核心代码,先看下面的代码块,为compose的源码
export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }
//上面的代码都不管
//获取funcs数组的最后一个方法
  const last = funcs[funcs.length - 1]
  //这里将funcs数组从头到倒数第二个重新放到了一个新的数组里,倒数第一个已经在上面被拿出来了
  const rest = funcs.slice(0, -1)
  //点睛之笔来了:
  //这里对rest数组进行了一次从右到左的数组包裹:
  //该函数在源码中的注释中给了一个简洁的表达:compose(f, g, h) is identical to doing * (...args) => f(g(h(...args))).
  return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
}

PS:代码依旧十分的干练………..
这里对(…args) => rest.reduceRight((composed, f) => f(composed), last(…args))进行详细的解释(由于ES6语法不熟,这里墨迹了很久)

首先看一下…args,这个并非指funcs这个形参,而是调用这个方法时传入的实参。注意看compose方法的调用:compose(…chain)(store.dispatch),…chain对应的是funcs,而store.dispatch则对应的是…args。
然后再看一下rest.reduceRight(function(pre,current,index,array),start),这个方法。

解释下两个参数
1、function为方法回调,回调回来的4个参数分别为:
pre:上一次执行完毕之后返回的结果;
current:当前要用的数组中的数据;
index:当前数组的数据的下标;
array:当前的数组。
2、第一次执行是pre的值

OK,这时我们看一下源码,pre对应的值为last(…args),实际上就是chain这个dispatch链中的最后一个,也就是中间件的最后一个的diapatch,它调用了真正最原始的store.dispatch。即last(store.dispatch),由于chain链中的dispatch返回的仍然是个dispatch,所以第二次回调的pre又是一个dispatch,是last(store.dispatch)返回的结果。于是形成了一个组合函数链式调用:
middleware1Dispatch(middleware2Dispatch(middleware3Dispatch(store.dispatch())));
这样就形成一个dispatch,然后再调用的时候,可以保证从外到内一次调用,然后dispatch(action)的时候,又内至外又依次进行。
这里盗图一张:
原文
这里写图片描述

最后再看一下简单的redux-logger的中间件的代码:

const logger = store => next => action => {
    console.log('dispatch:',action);
    next(action);
    console.log('finish:',action);
}

由于最终的一个dispatch是一个链式的dispatch的组合函数,所以会又外至内的依次调用next(action)之前的代码和next(action)代码,当调用到最终的store.dispatch之后,会依次又内至外的调用next(action)之后的代码,由此形成了上方那个图,先由外至内,再由内至外。

OK,这里只是粗浅的解释了一下Redux中间件源码,对于其中高深的理解并未解释,比如里面用的组合函数、柯里化。这些巧妙的用法和干练的写法需要继续对ES6深入了解之后才可以陆续的理解。

最后放一下参考的一系列文章:

精益 React 学习指南 (Lean React)- 3.3 理解 redux 中间件

深入理解redux中间件
redux middleware 详解
浅谈redux 中间件的原理
(译)深入浅出Redux中间件
JS中的柯里化(currying)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值