createStore(reducer, preloadedState, enhancer) => {
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
// 所以第三种直接传入applyMiddleware(logger1, logger2),效果是一样的。
return enhancer(createStore)(reducer, preloadedState)
}
}
redux基础
我们先大概过一下redux暴露的几个方法。
createStore 一个工厂函数传入reducer,创建store,返回几个函数,主要是dispatch,getState,subscribe,replaceReducer,以及结合rx这种发布订阅库的symbol($$observable)
combineReducers 把单个的reducer组合成一个大的reducer。其实就是返回一个新的reducer函数,去遍历执行所有的子reducer,并返回state。
bindActionCreators 把我们写的一个js中的好多ActionCreator 通过遍历搞的一个对象里,并返回。
applyMiddleware 一个三阶函数,是用来改写store的dispatch方法,并把所有的中间件都compose串联起来,通过改写dispatch,来实现redux功能的拓展。
compose 一个组合多个middleware的方法,通过reduceRight方法(同理也可以是reduce),把传进来的middleware串成一条链,也可以看成回调接回调,一个预处理的方法。
redux-middleware
我们从redux中applyMiddleware使用入口开始研究。
中间件
为什么中间件要定义成这种三阶的样子呢,当然是中间件的消费者(applyMiddleware)规定的。
先通过一个小栗子看一下middleware的使用。
通过applyMiddleware执行可以得到一个store,store再通过react-redux中的provider注入。此时得到的store就是被改造了dispatch的。通过图来形象的解释一下:
可以看出redux在事件或者某个函数调用后,执行action(可能是bindActionCreators处理后的),由于bindActionCreator会去调用dispatch,
dispatch内部会把currenReducer执行,并把监听者执行。实现view更新。 但是经过applyMiddleware的包装,store里面的被封装,在调动action之后,执行封装后的dispatch就会经过一系列的中间件处理,再去触发reducer。
然后我们再通过研究源码,看他是怎么实现的封装dispatch。
思路可以从通过applyMiddleware创建store一点一点的看。
compose还是比较重要的
compose执行
chain中都是已经预置middlewareAPI参数后的二阶函数。执行传入的参数都是 形参next。
通过执行compose(...chain)(store.dispatch),last是最后一个中间件,执行并传入 store.dispatch, 返回一个只剩一阶的(action) => {}, 不过已经预置了next参数,也就是store.dispatch
然后last(...args)返回的结果传入reduceRight的回调, 对应形参是composed。
f是rest的最后一项, 执行并把 composed 传入,等同于f形参中的next... 得到的结果也是一阶函数,预置的next是last(...args) ...
以此类推。这样,就形成了一个嵌套多层的语句。 类似于
logger1(logger2(store.dispatch)
,当然这只是一个比喻。 只不过到第一个middleware的时候,是二阶函数传入next执行,得到一阶函数返回赋值给dispatch,这时的一阶函数已经变成了形似这样:经过compose之后的dispatch执行
返回的store中dispatch被修改,执行store.dispatch的时候,也就是这个函数执行.
当执行到next(action)的时候,会调用已经预置的next函数,也就是第二个中间件的(action) => {},依次类推。直到最后一个中间件,他的next函数是store.dispatch函数,执行并把action传入。
执行完最后一个中间件的next(action),也就是初始的dispatch。next后面的代码再执行,再逆向把中间件走一遍,直到第一个中间件执行完毕。 就会出现这种效果
用图形象点就是
这样redux middleware的执行流程就搞清楚了。
应用applyMiddleware的方式
createStore源码中有一个判断,
第一种先compose同理。一个参数的时候会返回applyMiddleware,变形之后也是一样的。
enhancer的用法很多种,不仅仅是applyMiddleware,比如Redux-Devtools, 都是利用了compose函数。自定义开发一些拓展功能还是很强大的... redux里的compose是处理三阶函数的,恰巧createStore, applyMiddleware都是三阶函数,都可以通过compose串联起来。不禁感叹函数式编程思维的强大啊。
应用异步action
actionCreator之前都是返回一个
{type: 'UPDATE', text: 'aaa'}
这样的简单对象。通过thunk中间件,可以处理返回function的情况。express中的middleware
用法
每次访问这个app应用的时候,都会执行
模拟
假总结
现在web的中间件概念,都区别于最早严格意义上的中间件,其实我们现在的很多编程思想都是借鉴的先驱提出的一些东西。JAVA中类似的是AOP,即面向切面编程,以补充OOP(面向对象)多个对象公用某些方法时造成的耦合。
目前js中见到的中间件思想用法都是差不多的,只有调用next,程序才会继续往下执行,没有next,可以抛出异常等。只不过redux使用的函数式编程思想,用法偏函数式一些。
demo代码我会放到middleware-demo目录里,可以clone下来操作一番。链接
先到这,下次衍生就是函数式编程了。