Vesper@之一

【偷裤衩】Dan佬的Redux(续1)😎

👉 引言:这是一个源码共读的系列文章,我管它叫偷裤衩,顾名思义,非常形象,妙不可言,不可多言,回味无穷。

  1. 简单聊一下【偷裤衩】的价值:
  1. 简单聊一下【偷裤衩】的步骤:

开始🚀

废话不多说,续1是接《Dan佬的Redux》的,上期末尾我们聊到了Redux中的其他模块,applyMiddleware,还有combineReducers以及Action模块这些,这续1,我们就来盘一盘applyMiddleware

我们直接进入applyMiddleware.ts,除了类型相关代码,逻辑代码仅仅30行不到,还是老规矩,我们先上源码图(由于原本代码就不多,所以我只省略了类型声明的部分)。

code1

简单的思维拆解1️⃣

applyMiddleware的代码并不复杂,一张图就基本能概括。

code2

深入理解2️⃣

export default function applyMiddleware(
  ...middlewares: Middleware[]
): StoreEnhancer<any> {
  return createStore => (reducer, preloadedState) => {
    const store = createStore(reducer, preloadedState)  // [!code focus]
    let dispatch: Dispatch = () => {  // [!code highlight]
      throw new Error(
        'Dispatching while constructing your middleware is not allowed. ' +
          'Other middleware would not be applied to this dispatch.'
      )
    }

    const middlewareAPI: MiddlewareAPI = {
      getState: store.getState,
      // 这里是传入middleware的 dispatch
      dispatch: (action, ...args) => dispatch(action, ...args) // [!code highlight]
    }
    const chain = middlewares.map(middleware => middleware(middlewareAPI))  // [!code focus]
    // 这里覆写 dispatch
    dispatch = compose<typeof dispatch>(...chain)(store.dispatch)  // [!code focus]

    return {
      ...store,
      dispatch
    }
  }
}

::: details 点我查看applyMiddleware的套娃解读🔝 这里applyMiddleware函数接收所有的中间件函数(即...middlewares),特别注意一点是有一个套娃return,这里applyMiddleware直接返回了一个返回对象的函数的函数((...middlewares) => (createStore) => (reducer, preloadedState) => ({...store, dispatch})这么个套娃法的),以便为了一层一层传入参数,并将每一层的参数闭包传入到最后的一层中进行使用,最后是用compose处理后的dispatch来复写了applyMiddleware中定义的dispatch,注意compose的最后一个调用,传入的原始dispatch(store.dispatch)。 :::

::: details 点我查看applyMiddleware的逻辑解读🔝 实际上applyMiddleware主要是通过对createStore返回值的覆写,把中间件应用到状态中的,在最里层,是通过调用createStore(reducer, preloadedState)生成状态对象(store),然后将状态获取方法(getState)和middlewareAPI内部定义的dispatch方法(注意这里并不是createStore返回的dispatch)将外层的dispatch传入给每一个中间件(middleware(middlewareAPI)),以便中间件能拿到dispatch的闭包引用进行相应的逻辑处理,最后将compose合并后的每一个中间件调用后返回的函数和最后传入原始的store.dispatch的函数赋值给了第一个dispatch,覆写了applyMiddleware返回值(即原始的createStore返回值)中的dispatch方法,这样也就在createStore返回的dispatch中,应用了咱们所有的中间件了。 :::

export default function compose(...funcs: Function[]) {
  return funcs.reduce(
    (a, b) =>
      (...args: any) =>
        a(b(...args))
  )
}

::: details 点我查看compose的解读🔝 其实一些熟悉一些工具库或者函数式编程的小伙伴,应该对compose并不陌生,compose主要就是处理函数的合并的,这里是以一种正序调用的顺序来合并的,也就是说,咱们之前在applyMiddleware中通过compose合并后的dispatch就是将所有的中间件处理后返回的函数合并起来,最终由store.dispatch来包裹,在调用时,就会从最里面的一层开始执行,依次向外的调用(类似这样a(b(c(d(e()))))),也就在最终store.dispatch前,先正序执行了所有中间件(middleware)。 :::

至于中间件干了什么,我们不得而知,但是暴露给了中间件getStatedispatch的引用,而中间件也是返回一个函数回调,实际可想而知又是一个闭包的利用,最终是在applyMiddleware处理后返回的dispatch中,当调用dispatch时触发回调函数的,不得不说,这真的很妙~


以上就是applyMiddleware的主要逻辑了,可以看到整个的实现充斥了非常多的闭包,scope chainjs特性的利用,这次的设计便是中间件的思想,有一个小小的编程范式的利用,就是函数式编程,其中compose函数就很有代表性,并且中间件的思想其实也挺函数式的。后续Redux还有combineReducers等模块,我们下次继续吧~


🖥️写在最后:

以上就是这期【偷裤衩】的全部内容了,阅读源码就像是读书,沿着各个源码作者的编码思路进行探索的过程,这有助于帮助自己偷师百家,成为仙道巅峰之人。