Redux源码浅析系列(四):`applyMiddleware`

前面主要介绍了createStorecombineReducers,compose的实现原理,下面,我们看一下 redux中最有意思的中间件部分applyMiddleware
applyMiddleware代码很简洁,但是含义很广泛。我们来一起看一下:

首先,我们先来重温一下中间件的使用方法:

  • 调用中间件
    来看一下createStore的源码
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }
  //enhancer必须是一个高阶函数
  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }
    //增强器
    return enhancer(createStore)(reducer, preloadedState)
  }

enhancer是中间件,当createStore的第二个参数是function且没有第三个参数时,也可以直接在第二个参数设置中间件。那么中间件就有两种设置方式

createStore(reducer,initState,applyMiddleware(ThunkMiddleware));

或者

createStore(reducer,applyMiddleware(ThunkMiddleware));

熟悉了用法之后,我们来看一下applyMiddleware的实现方式:

export default function applyMiddleware(...middlewares) {
  return (createStore) => (reducer, preloadedState, enhancer) => {
    // 生成一个store 
    const store = createStore(reducer, preloadedState, enhancer)
    let dispatch = store.dispatch
    let chain = []
    //简陋版的store,里面包含了`getState`和`dispatch`两个方法。
    const middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    }
    //将middlewareAPI作为参数注入到每个middleware中去,执行middleware, 返回一个新的链。
    //中间件函数(store)=>next=>action.
    // chain  [next=>action,...];
    //这个 next 其实store.dispatch.   而`action`就是`dispatch`的action
    chain = middlewares.map(middleware => middleware(middlewareAPI))

    //我们假设有三个中间件,fn1,fn2,fn3,那么下面代码等同于  dispatch=fn1(fn2(fn3(store.dispatch)));
    //可以发现,中间件所组成的dispatch 其实就是一个执行过fn1,fn2,fn3的函数。
    //所以,每个中间件在遇到不是自己处理范围之内的action的时候,会使用next(action),将其传递给下一个中间件。
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

再来一段redux-thunk中间件的源码做为参考,一起来看一下

function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }

    return next(action);
  };
}

我们从中间件的代码开始入手,一点点剖析applyMiddleware

在中间件的代码中有这样一段代码:({dispatch,getState})=>next=>action=>{};
结合上面applyMiddleware的源码来看呢,很容易发现({dispatch,getState})指的就是middlewareAPI,是一个简陋版的store,而action就是我们dispatchaction。那么这个next是什么呢?

在执行chain = middlewares.map(middleware => middleware(middlewareAPI))的时候,我们将 middlewareAPI作为参数传进去,对应着中间件代码中的({dispatch,getState})=>这一步。则执行完的chain则表示[(next)=>action=>{},...]

继续向下看,
dispatch = compose(...chain)(store.dispatch)
这时候,可以发现,其实next参数其实就是原始的store.dispatch的这个dispatch

那么,applyMiddleware在多中间件的场景下,是如何工作的呢
我们来看一下dispatch = compose(...chain)(store.dispatch)
假设我们有fn1,fn2,fn3三个中间件,那么以上函数就可以拆解为
dispatch=fn1(fn2(fn3(store.dispatch)));

我们举个例子来看:

    const a = next => () => {
        console.log('a pre');
        next(); //执行b(c(d()));
        console.log('a after');
    }

    const b = next => () => {
        console.log('b pre');
        next(); // 执行c(d());
        console.log('b after');
    }

    const c = next => () => {
        console.log('c pre');
        next(); //执行  d(); 
        console.log('c after');
    }

    const d = () => {
        console.log('Hello World');
    }

    function compose(...funcs) {
        return funcs.reduce((a, b) => (...args) => a(b(...args)))
    }

    console.log(compose(a, b, c)(d)())

打开浏览器的console,可以发现执行结果为

a pre
b pre
c pre
Hello World
c after
b after
a after

通过compose函数,将compose(a,b,c)(d)()合成为a(b(c(d)))();
则首先执行a(),然后再控制台上打印出a pre,然后执行next(),此时的next其实就是b(c(d)),然后执行b(),打印出b pre,随后在执行next(),这时的next表示的是c(d),然后执行c(),打印出c pre,随后在执行next(),这时的next表示的是d,然后执行d(),打印出helloWorld.
随后继续执行,打印出c after,然后是b after,a after

这个例子很好的解释了中间件的执行过程。我们的compose函数只是将fn1,fn2,fn3组成了fn1(fn2(fn3()))函数,不会立即运行最里面的函数,只有fn1执行了next之后,才会执行fn2,同样,只有fn2执行了next之后,fn3才会执行。以此类推。假如,我们在某个层级不执行next了,那么这个链就断了。

所以,我们在使用redux-logger这个中间件的时候,必须要把它放在middlewares的最右边,就是因为担心后面的某个中间件万一不执行next了,整个链就断了,那么即使他在前面执行了next,也是没有用的。

这也要求我们,在编写中间件的时候,都要执行一次next(action),因为只要每个中间件都常规的执行next(action),就能保证这个链不断,原始的dispatch就可以一直分发下去了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值