applyMiddleware.js
applyMiddleware帮助我们将第三方的中间件,转换成能增强store的enhancer。
下面是一个运用redux-thunk的例子:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducer';
const store = createStore(reducer, applyMiddleware(thunk));
export store
可以看到applyMiddleware接收中间件,执行返回的结果就是一个能增强store的enhancer。
还记得在createStore.js里有这么一段代码吗?:
if(typeof enhancer !== undefined){
if(typeof enhancer !== 'function'){
throw new Error('Expected the enhancer to be a function.');
}
return enhancer(createStore)(reducer, preloadedState)
}
可以看到,在createStore中,如果发现传入了合法的enhancer,就会将逻辑交给enhancer执行,让它返回增强后的store。
结合两段代码,我们可以推测出applyMiddleware函数的大致模样:
// 第一层,传入中间件,返回一个enhancer,供createStore使用
function applyMiddleWare(...middlewares){
// 第二层,供createStore内部调用
return (createStore) => {
// 第三层,供createStore内部调用
return (reducer, preloadedState) => {
// ....具体逻辑
return store; // 返回增强后的store
}
}
}
下面我们看一下applyMiddleware.js里的具体源码:
import { compose } from 'redux'
export default function applyMiddleware(...middlewares){
return (createStore) => {
return (reducer, preloadedState) => {
const store = createStore(reducer, preloadedState)
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: (action, ...args) => dispatch(action, ...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
}
可以看到,这个函数做了以下几件事情:
- 调用传入的createStore,构建一个基础的store
- 定一个dispatch方法,这个方法被调用就会报错,注意这个方法在下面会被改写!
- 定义一个middlewareAPI对象,里面包含getState方法和会报错的dispatch方法
- 使用map方法,将中间件都执行一次,参数是上一步的middlewareAPI对象,得到chain数组
- 使用compose方法,将chian数组中的所有函数进行组合,得到一个函数,并赋值给dispatch(改写dispatch)
- 返回增强后的store
下面重点讲解会让人迷惑的第4和第5步。
第4步:使用map方法,将中间件都执行一次,参数是上一步的middlewareAPI对象,得到chain数组
为了方便理解,我们这里引入redux-thunk,从头开始分析,下面是redux-thunk的源码:
function createThunkMiddleware(extraArgument?) {
return function ({ dispatch, getState }) {
return function (next) {
return function (action) {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument)
}
return next(action)
}
}
}
}
const thunk = createThunkMiddleware()
export default thunk
可以看到导出的thunk是createThunkMiddleware()执行的结果,所以我们得到的thunk是:
const thunk = function ({ dispatch, getState }) {
return function (next) {
return function (action) {
if (typeof action === 'function') {
return action(dispatch, getState)
}
return next(action)
}
}
}
当applyMiddleware(thunk)时,applyMiddleware内部得到的middlewares就是:
当到第4步,会使用map方法,将中间件都执行,并传入middlewareAPI, 也就是
const chian = [thunk].map(middleware => middleware(middlewareAPI))
这里会执行thunk, 并传入dispatch和getState,执行后,会得到这么一个函数:
const chainThunk = function (next){
return function (action) {
if (typeof action === 'function') {
return action(dispatch, getState)
}
return next(action)
}
}
所以我们可以知道chain的值:
第5步:使用compose方法,将chian数组中的所有函数进行组合,得到一个函数,并赋值给dispatch(改写dispatch)
在redux源码分析(3) — compose里,我们已经讲解了compose的作用。
我们将dispatch = compose(...chain)(store.dispatch)分成两部分执行
- 第一步
// 上一步得到的chain
chian = [chainThunk]
compose(...chain)
compose执行后,我们可以得到这么一个函数, 我们将它命名为composeThunk:
const composeThunk = (...args) => chianThunk(...args)
- 第二步
dispatch = composeThunk(store.dispatch)
其实注意观察,这里我们调用的composeThunk,本质上也是chianThunk, 所以实际上:
const chainThunk = function (next){
return function (action) {
if (typeof action === 'function') {
return action(dispatch, getState)
}
return next(action)
}
}
dispatch = chainThunk(store.dispatch)
所以我们得到的新dispatch就是:
function (action) {
if (typeof action === 'function') {
return action(dispatch, getState)
}
return next(action)
}
在applyMiddleware的最后:
return {
...store,
dispatch
}
会返回一个被强化了dispatch的store,所以所谓的enhancer,其实就是用来强化store的dispatch的。
最后,还有需要补充的是,我们这里只举例了使用一个中间件的情况,其实大多数项目中,我们都会有多个中间件。
多个中间件的情况
假如我们有以下两个中间件:
const middleware1 = ({dispatch, getState}) => {
return (next) => {
return (action) => {
...do something
next(action)
}
}
}
const middleware2 = ({dispatch, getState}) => {
return (next) => {
return (action) => {
...do something
next(action)
}
}
}
我们会在第4步后,得到这么两个函数:
const chain1 = function (next) => {
return (action) => {
...do something
next(action)
}
}
const chain2 = function (next) => {
return (action) => {
...do something
next(action)
}
}
// chain
chain = [chain1, chain1]
当这个chain被第5步compose(...chain)(store.dispatch)调用时,实际情况如下
compose(...chain)(store.dispatch)
等价于
compose(chain1, chain2)(store.dispatch)
等价于
chain1(chain2(store.dispatch))
最后增强后的dispatch就是上方代码的执行结果,每个中间件最后返回的(action) => ...都会成为下一个中间件的next参数。
所以,当一个中间件做完事情后,就可以调用next,将action传递给下一个中间件。
最后贴一下redux-thunk的带注释代码,帮助理解中间件:
// 第一层 返回一个接受dispatch 和 getState的函数
// 这层供applyMiddleware里传入middlewareAPI调用
const thunk = ({ dispatch, getState }) => {
// 第二层 返回一个接受next的函数
// 这层供compose函数调用
// next参数是上一层的(action) => ....
return function (next) {
// 第三层 返回一个接受action的函数
// 这层也就是下一个中间件的next参数
// 当只有一个中间件时,这层就是被增强后的dispatch
return function (action) {
// 如果action类型是函数的话,执行它
if (typeof action === 'function') {
return action(dispatch, getState)
}
// 不是函数的类型,交给下一个中间件执行
return next(action)
}
}
}
export default thunk
结语
applyMiddleware是redux中最为复杂的一个函数了,虽然代码量不多,但是运用闭包的技巧和函数式思想让人折服。
applyMiddleware.js
applyMiddleware帮助我们将第三方的中间件,转换成能增强store的enhancer。
下面是一个运用redux-thunk的例子:
可以看到applyMiddleware接收中间件,执行返回的结果就是一个能增强store的enhancer。
还记得在createStore.js里有这么一段代码吗?:
可以看到,在createStore中,如果发现传入了合法的enhancer,就会将逻辑交给enhancer执行,让它返回增强后的store。
结合两段代码,我们可以推测出applyMiddleware函数的大致模样:
下面我们看一下applyMiddleware.js里的具体源码:
可以看到,这个函数做了以下几件事情:
下面重点讲解会让人迷惑的第4和第5步。
第4步:使用map方法,将中间件都执行一次,参数是上一步的middlewareAPI对象,得到chain数组
为了方便理解,我们这里引入redux-thunk,从头开始分析,下面是redux-thunk的源码:
可以看到导出的thunk是createThunkMiddleware()执行的结果,所以我们得到的thunk是:
当applyMiddleware(thunk)时,applyMiddleware内部得到的middlewares就是:
当到第4步,会使用map方法,将中间件都执行,并传入middlewareAPI, 也就是
这里会执行thunk, 并传入dispatch和getState,执行后,会得到这么一个函数:
所以我们可以知道chain的值:
第5步:使用compose方法,将chian数组中的所有函数进行组合,得到一个函数,并赋值给dispatch(改写dispatch)
在redux源码分析(3) — compose里,我们已经讲解了compose的作用。
我们将
dispatch = compose(...chain)(store.dispatch)分成两部分执行compose执行后,我们可以得到这么一个函数, 我们将它命名为composeThunk:
其实注意观察,这里我们调用的composeThunk,本质上也是chianThunk, 所以实际上:
所以我们得到的新dispatch就是:
在applyMiddleware的最后:
会返回一个被强化了dispatch的store,所以所谓的enhancer,其实就是用来强化store的dispatch的。
最后,还有需要补充的是,我们这里只举例了使用一个中间件的情况,其实大多数项目中,我们都会有多个中间件。
多个中间件的情况
假如我们有以下两个中间件:
我们会在第4步后,得到这么两个函数:
当这个chain被第5步
compose(...chain)(store.dispatch)调用时,实际情况如下最后增强后的dispatch就是上方代码的执行结果,每个中间件最后返回的
(action) => ...都会成为下一个中间件的next参数。所以,当一个中间件做完事情后,就可以调用next,将action传递给下一个中间件。
最后贴一下redux-thunk的带注释代码,帮助理解中间件:
结语
applyMiddleware是redux中最为复杂的一个函数了,虽然代码量不多,但是运用闭包的技巧和函数式思想让人折服。