你该知道的redux中间件

441 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

首先我们来了解一下redux的几个基本概念:

  • Action 是把数据从应用传到 store 的有效载荷。它是 store 数据的唯一来源。一般来说你会通过 store.dispatch()action 传到 store
  • Reducers 指定了应用状态的变化如何响应 actions 并发送到 store
  • Store维护了想要存储的 state 以及用来满足 ActionReducers 使用的 API;

redux的一个粗略的是处理过程就是:

image.png

什么是redux中间件?

redux中间件其实是提供了位于 action 被发起之后,到达 reducer 之前的扩展点,暂时简单理解为这样(其实不然,实际详细结构往后看oVo!)

image.png

中间件格式

箭头函数风格:

({getState, dispatch}) => next =>  action => { ... }

相当于

({getState, dispatch}) => {
    return next => {
        return action => { ... }
    }
}

至于为什么要这种格式,可以看下文。

applyMiddleware

中间件的格式是固定的,那么它是怎么发挥作用的呢?先来看一下applyMiddleware的源码实现:

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 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,
                dispatch: (action, ...args) => dispatch(action, ...args)
            }
            const chain = middlewares.map(middleware => middleware(middlewareAPI)) // 每一个中间件传入MiddlewareAPI
            dispatch = compose<typeof dispatch>(...chain)(store.dispatch)

            return {
                ...store,
                dispatch
            }
        }

applyMiddleware的源码可以看出,该函数的主要作用就是将middleware队列依次处理,以对象形式传入getStatedispatch;然后中间件队列中每个中间件变成了以下格式:

next => action => { ... }

然后再作为参数由compose函数处理。

compose

compose的实现源码如下:

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)))
}

return的处理方式可以看出,中间件队列经过reduce处理以后会是这个样子: 举个栗子:

[m1, m2, m3] 

compose处理后:

(...args) => m1(m2(m3(...args)))

可以看出m3的返回结果作为参数传给了m2执行,m2的返回结果作为参数传给了m1执行。由此可以看出,中间件队列中中间件的执行顺序是从右到左倒序执行的m3 -> m2 -> m1且前一个函数返回结果作为参数给下一个函数。在applyMiddlewarestore.dispatch将作为compose返回函数的参数传入,而store.dispatch的返回值就是要 dispatchaction。由此可以做一些定制化的操作了,比如简单介绍一下以下栗子:

m1

({dispatch, getState}) => (next) => (action) => { 
    console.log('m1 pre next')
    let result = next(action);
    console.log('m1 after next')
    return result;
}

m2

({dispatch, getState}) => (next) => (action) => { 
    // action经过此中间件提交之前,do something。。。

    console.log('m2 pre next')
    let result = next(otherAction);
    console.log('m2 after next')
    // action经过此中间件提交之后,do something。。。
    return result;
}

compose处理以后伪代码

(...args) => m1(m2(...args))

然后store.dispatch作为参数传入,执行结果变为:

m1(m2(store.dispatch)

然后m2执行以后:

m1((action) => {
    console.log('m2 pre next')
    let result = next(otherAction);
    console.log('m2 after next')
    return result;
})

类似等价于:

(() => {
    console.log('m1 pre next')
    let result = (() => {
        console.log('m2 pre next')
        let result = store.dispatch(action);
        console.log('m2 after next')
        return result
        })()
    console.log('m1 after next')
    return result;
})()

其结构有一个较为通俗的叫法:洋葱模型

洋葱模型

结构就是这样

{'m1 pre next'{'m2 pre next'{store.dispatch}'m2 after next'}'m1 after next'}

用图表示就是

image.png 这才是中间件的真实结构~