前言
在初步了解redux中间件演变过程之后,继续研究redux如何将中间件结合。上次将中间件与redux硬结合在一起确实有些难看,现在就一起看看redux如何加持中间件。
- 中间件执行过程
希望借助图形能帮助各位更好的理解中间件的执行情况。
- redux如何加持中间件
现在是时候看看redux是如何将中间件结合了,我们在源码中一探究竟。
* @param {function} [enhancer] the store enhancer. you may optionally specify it * to enhance the store with third-party capabilities such as middleware, * time travel, persistence, etc. the only store enhancer that ships with redux * is `applymiddleware()`. * * @returns {store} a redux store that lets you read the state, dispatch actions * and subscribe to changes. */ export default function createstore(reducer, preloadedstate, enhancer) { if ( (typeof preloadedstate === 'function' && typeof enhancer === 'function') || (typeof enhancer === 'function' && typeof arguments[3] === 'function') ) { throw new error( 'it looks like you are passing several store enhancers to ' + 'createstore(). this is not supported. instead, compose them ' + 'together to a single function' ) } if (typeof preloadedstate === 'function' && typeof enhancer === 'undefined') { enhancer = preloadedstate // 如果初始化state是一个函数,则认为有中间件 preloadedstate = undefined } if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { throw new error('expected the enhancer to be a function.') } return enhancer(createstore)(reducer, preloadedstate) }
如果createstore第二个参数是函数(第二,第三都是函数会抛异常),则redux认为第二个参数是调用applymiddleware函数的返回值(注释有说明)。
根据return enhancer(createstore)(reducer, preloadedstate),说明applymiddleware返回了一个函数,该函数内还返回了一个函数。那么接下来从applymiddleware源码中一探究竟。
export default function applymiddleware(...middlewares) { // 将所有中间件存入middlewares数组 return createstore => (...args) => { // 返回函数以createstore为参数,args即[reducer, preloadedstate] const store = createstore(...args) // 创建一个store let dispatch = () => { // 定义一个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) // middlewareapi的dispatch属性指向一个匿名函数,该函数内部会执行外部dispatch变量指向的那个函数。 } const chain = middlewares.map(middleware => middleware(middlewareapi)) // 执行每个中间件,顺带检查是否有中间件调用传入参数中的dispatch,如果有则抛出异常 dispatch = compose(...chain)(store.dispatch) // 将chain展开传入compose,然后执行返回的函数,传入store.dispatch,最后将所有中间件组合成最终的中间件,并将dispatch变量指向这个中间件。 // 由于dispatch变量的更改,它原来指向的匿名函数现在没有任何变量指向它,会被垃圾回收。
// 误区:调用middlewareapi的dispatch属性指向的函数时,内部的dispatch会指向原来抛出异常的匿名函数。这是错误的,在调用middlewareapi的dispatch属性所指向的函数时,
// 会寻找dispatch变量,函数内部找不到就向外部作用域寻找,然后找到外部dispatch,而此时外部的dispatch指向最终的中间件,所以会调用最终的中间件。这对于理解redux-thunk非常重要。
return { ...store, dispatch // 覆盖store中dispatch变量 } } }
上面的代码中还有一点疑惑,compose函数是什么样子,那么我们再探compose。
* @param {...function} funcs the functions to compose. * @returns {function} a function obtained by composing the argument functions * from right to left. for example, compose(f, g, h) is identical to doing * (...args) => f(g(h(...args))). 可以发现,和我们之前写的代码效果一模一样 */ export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args) => a(b(...args))) }
也许你对数组的reduce方法不是很熟,上篇文章篇幅也比较饱满。那么这儿简单讲解下:
[1, 2, 3, 4].reduce((a, b) => { console.log(a, b); return a + b }) // 1 2 可以发现第一次执行,我们拿到数组的第1,2个变量 // 3 3 拿到上次返回的结果和第3个变量 // 6 4 拿到上次返回的结果和第4个变量
最后结果为10,没有打印所以看不出。当然数组存储的也可能是对象,在reduce函数执行时,拿到每个变量的副本(浅拷贝),然后根据你的代码做对应的事。在这就以上篇文章的中间
件为例,再加入logmiddleware3(和logmiddleware2类似,只是将打印的数字部分改为3而已),看看compose函数执行过程。
[logmiddleware3, logmiddleware2, logmiddleware].reduce((a, b) => (...args) => a(b(...args)))
// 假定compose函数传入的参数为store.dispatch,则有以下结果:
// (logmiddleware3, logmiddleware2) => (...args) => logmiddleware3(logmiddleware2(...args)) 这里args[0]为logmiddleware(store.dispatch)返回的中间件
// (logmiddleware3(logmiddleware2(...args)), logmiddleware) => (...args) => logmiddleware3(logmiddleware2(logmiddleware(...args))) 这里的args[0]为store.dispatch
// 最后返回(...args)=> logmiddleware3(logmiddleware2(logmiddleware(...args))) ,接着执行该函数,传入store.dispatch,也就产生了最终的中间件
现在对于redux结合过程已经有了一定的认识,是时候看看别人的中间件了,对比我们自己的中间件,也许有不同的收获。
- redux-thunk
至此我们写的中间件都比较好理解,是时候认识下redux-thunk了。它又会有什么特别之处了,让我们一起看看源码。
function createthunkmiddleware(extraargument) { // 这里extraargument完全没用到 return ({ dispatch, getstate }) => next => action => { // 这里的dispatch如果有疑惑,请看上面您可能感兴趣的文章:
如您对本文有疑问或者有任何想说的,请 点击进行留言回复,万千网友为您解惑!
相关文章:
-
-
一、作用及应用场景call和apply是function的方法,他的第一个参数是this,第二个是function的参数。call 和 apply 都是为了改变... [阅读全文]
-
Node.js实现HTML表单文件上传模块加载单文件上传服务器代码HTML代码同类多文件上传不同类多文件上传最近... [阅读全文]
-
文章目录分布式系统两个重要的含义集中式系统为什么用分布式系统**分布式系统挑战**异构性缺乏全球时钟一致性普遍的... [阅读全文]
-
开发时配置vue.config.js解决跨域module.exports = { devServer: {... [阅读全文]
-
云音乐曲库缓存随着多年的实践、改善,结合曲库数据的特点,形成自有的一套缓存使用体系,并能够取得了很好的效果。在工... [阅读全文]
-
1.在Windows上安装Nodejs#查看版本$ npm -v#升级 npmcnpm install npm ... [阅读全文]
-
简介ubuntu缺省的配置的源并不是国内的服务器,下载更新软件都比较慢,本文介绍如何设置源列表,选择比较快的源以节省下载时间。配置步骤1. 备份源列表sudo ... [阅读全文]
-
在学习js过程中,经常会遇到同样一个功能点 这样实现也可以,那样实现也可以。但是哪个方式最优呢?自己写了一个简短的proferencescompare ... [阅读全文]
-
本文实例为大家分享了js轮播图的实现代码,供大家参考,具体内容如下需求:自动轮播,鼠标移入轮播停止、移出继续,小圆点点击切图,左右箭头切图效果图:思路通过编写过... [阅读全文]
-
网友评论