Redux 源码解析(三): compose & applyMiddleware API

Redux 源码解析(三): compose & applyMiddleware API

前言

Redux 源码系列转眼间就来到最后一篇了,本篇要来介绍最后剩下的两个 API:compose 和 applyMiddleware

正文

1. compose 源码解析

compose 函数还是比较常见的,属于函数式编程的标配,作用是将

compose(f1, f2, f3, f4)

变成

f1(f2(f3(f4())))

的调用

然而在 Redux 源码当中其实这个 compose 主要是用于实现 applyMiddleware 时对多个 middleware 的包装

1.1 方法签名

所以接下来我们先看看 compose 的方法签名

  • /src/compose.ts(阅读笔记:/src/compose.ts/exports.ts
type Func<T extends any[], R> = (...a: T) => R

export default function compose(): <R>(a: R) => R;

export default function compose<F extends Function>(f: F): F;

/* two functions */
export default function compose<A, T extends any[], R>(
  f1: (a: A) => R,
  f2: Func<T, A>
): Func<T, R>;

/* three functions */
export default function compose<A, B, T extends any[], R>(
  f1: (b: B) => R,
  f2: (a: A) => B,
  f3: Func<T, A>
): Func<T, R>;

/* four functions */
export default function compose<A, B, C, T extends any[], R>(
  f1: (c: C) => R,
  f2: (b: B) => C,
  f3: (a: A) => B,
  f4: Func<T, A>
): Func<T, R>;

/* rest */
export default function compose<R>(
  f1: (a: any) => R,
  ...funcs: Function[]
): (...args: any[]) => R;

export default function compose<R>(...funcs: Function[]): (...args: any[]) => R;

这里可以看出 compose 的核心在于组合多个单参数的函数,然后以最后一个函数的方法签名作为导出函数的方法签名

1.2 源码实现

compose 的源码还是比较简单的,过一下就行了

  • /src/compose.ts(阅读笔记:/src/compose.ts/implements.ts
export default function compose(...funcs: Function[]) {
  if (funcs.length === 0) {
    // infer the argument type so it is usable in inference down the line
    return <T>(arg: T) => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  return funcs.reduce(
    (a, b) =>
      (...args: any) =>
        a(b(...args))
  )
}

2. applyMiddleware 源码解析

相对于 compose 来说,applyMiddleware 才是我们更关注的 api,用于为 store 添加中间件的方法

2.1 类型定义

接下来我们先来看看相关的类型定义

  • /src/types/middleware.ts(阅读笔记:/src/types/middleware.ts
import { Dispatch } from './store'

export interface MiddlewareAPI<D extends Dispatch = Dispatch, S = any> {
  dispatch: D
  getState(): S
}

export interface Middleware<
  _DispatchExt = {}, // TODO: remove unused component (breaking change)
  S = any,
  D extends Dispatch = Dispatch
> {
  (api: MiddlewareAPI<D, S>): (
    next: D
  ) => (action: D extends Dispatch<infer A> ? A : never) => any
}

很简单,一个是中间件的第一个参数的类型,一个则是中间件的方法签名

2.2 方法签名

接下来我们看看 applyMiddleware 的方法签名

  • /src/applyMiddleware.ts(阅读笔记:/src/applyMiddleware.ts/exports.ts
import compose from './compose'
import { Middleware, MiddlewareAPI } from './types/middleware'
import { AnyAction } from './types/actions'
import {
  StoreEnhancer,
  Dispatch,
  PreloadedState,
  StoreEnhancerStoreCreator
} from './types/store'
import { Reducer } from './types/reducers'

export default function applyMiddleware(): StoreEnhancer;

export default function applyMiddleware<Ext1, S>(
  middleware1: Middleware<Ext1, S, any>
): StoreEnhancer<{ dispatch: Ext1 }>;

export default function applyMiddleware<Ext1, Ext2, S>(
  middleware1: Middleware<Ext1, S, any>,
  middleware2: Middleware<Ext2, S, any>
): StoreEnhancer<{ dispatch: Ext1 & Ext2 }>;

export default function applyMiddleware<Ext1, Ext2, Ext3, S>(
  middleware1: Middleware<Ext1, S, any>,
  middleware2: Middleware<Ext2, S, any>,
  middleware3: Middleware<Ext3, S, any>
): StoreEnhancer<{ dispatch: Ext1 & Ext2 & Ext3 }>;

export default function applyMiddleware<Ext1, Ext2, Ext3, Ext4, S>(
  middleware1: Middleware<Ext1, S, any>,
  middleware2: Middleware<Ext2, S, any>,
  middleware3: Middleware<Ext3, S, any>,
  middleware4: Middleware<Ext4, S, any>
): StoreEnhancer<{ dispatch: Ext1 & Ext2 & Ext3 & Ext4 }>;

export default function applyMiddleware<Ext1, Ext2, Ext3, Ext4, Ext5, S>(
  middleware1: Middleware<Ext1, S, any>,
  middleware2: Middleware<Ext2, S, any>,
  middleware3: Middleware<Ext3, S, any>,
  middleware4: Middleware<Ext4, S, any>,
  middleware5: Middleware<Ext5, S, any>
): StoreEnhancer<{ dispatch: Ext1 & Ext2 & Ext3 & Ext4 & Ext5 }>;

export default function applyMiddleware<Ext, S = any>(
  ...middlewares: Middleware<any, S, any>[]
): StoreEnhancer<{ dispatch: Ext }>;

看起来很复杂,其实本质上就是可以传入多个中间件并封装成一个 enhancer 就对了

2.3 源码实现

实际上,enhancer 名字看起来很高大上,其实没有那么复杂,且听我娓娓道来

  • /src/applyMiddleware.ts(阅读笔记:/src/applyMiddleware.ts/implements.ts
export default function applyMiddleware(...middlewares: Middleware[]): StoreEnhancer<any> {
  return (createStore: StoreEnhancerStoreCreator) =>
    <S, A extends AnyAction>(reducer: Reducer<S, A>, preloadedState?: PreloadedState<S>) => {
      const store = createStore(reducer, preloadedState)
      let dispatch: Dispatch = () => {
        // throw error
      }

      const middlewareAPI: MiddlewareAPI = {
        getState: store.getState,
        dispatch: (action, ...args) => dispatch(action, ...args),
      }
      const chain = middlewares.map((middleware) => middleware(middlewareAPI))
      dispatch = compose<typeof dispatch>(...chain)(store.dispatch)

      return {
        ...store,
        dispatch,
      }
    }
}

首先定义一个基础的 dispatch 方法,但是禁止中间件在创建的过程就立即调用

      let dispatch: Dispatch = () => {
        // throw error
      }

接下来定义一个 api,负责传递 getState 和 dispatch 方法给每个中间件

      const middlewareAPI: MiddlewareAPI = {
        getState: store.getState,
        dispatch: (action, ...args) => dispatch(action, ...args),
      }

接下来就是将 api 传入中间件并用 compose 串起来

      const chain = middlewares.map((middleware) => middleware(middlewareAPI))
      dispatch = compose<typeof dispatch>(...chain)(store.dispatch)

也就是说当我们执行新的 dispatch 的时候,会由前至后,调用第一个 middleware 的 dispatch,而如果中间件调用 next 才会将 action 传递给下一个中间件来调用

结语

redux 的源码真的是相对简单很多,与网上说的一样非常适合初学者来学习,不过本篇也算是一个特别难的逻辑在于使用 compose 组合中间件的部分,因为中间件是一个有三层参数的科里化函数,还是比较复杂的。

Redux 源码系列就到这里结束啦,下面作者会在继续找找一些常用非常重要的库来带大家解析源码啦~

其他资源

参考连接

TitleLink
applyMiddleware - Reduxhttps://redux.js.org/api/applymiddleware
redux - Githubhttps://github.com/reduxjs/redux

阅读笔记参考

https://github.com/superfreeeee/Blog-code/tree/main/source_code_research/redux-4.1.1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值