Redux中间件源码理解
来源:互联网 发布:java redis 清除缓存 编辑:程序博客网 时间:2024/06/04 18:17
由于近期在了解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)
- Redux中间件源码理解
- redux-applyMiddleware实现理解+自定义中间件
- redux深入理解之中间件(middleware)
- redux深入理解之中间件(middleware)
- redux深入理解之中间件(middleware)
- redux的中间件
- 打造Redux中间件
- 自定义redux中间件
- redux添加中间件
- redux中间件的认识
- 理解 redux
- redux 理解
- 理解Redux
- 理解redux
- redux源码
- [译]深入浅出Redux中间件
- 浅谈redux 中间件的原理
- Redux:中间件与异步操作
- log4j2 实际使用详解
- TCP/IP 学习三-IP网际协议
- python安装第三方库超时报错“Read time out”的解决办法
- 基尼不纯度(Gini impurity)
- 整理菜单住方法(子类归属父类)
- Redux中间件源码理解
- 懒汉式单例设计模式同步问题
- PHP正则基础
- 5月集训Day1考试
- 晚期(运行期)优化
- OpenCV3.2+VS2013+Tesseract3.02.02配置
- 定位:position
- php致命错误:protocol error, got 'n' as reply type byte
- 早期(编译期)优化