软件技术学习笔记

个人博客,记录软件技术与程序员的点点滴滴。

Redux中间件机制与执行次序

一个框架如果有中间件机制,后续的可扩展性就很强,Redux就是这种一个数据处理框架。Redux中间件机制代码很短,但是理解起来可不是一句话讲得完。要看明白其中间件机制,主要看 createStore()applyMiddleware() 这两个函数。

1 Redux中间件机制

先看看代码:

function createStore(reducer, preloadedState, enhancer) {
    if (typeof enhancer !== 'undefined') {
        // 分支一: 使用增强器创建Store
        return enhancer(createStore)(reducer, preloadedState)
    }

    // 分支二: 原始Store创建过程
    // ....
    return {
        dispatch,
        subscribe,
        getState,
        replaceReducer,
        [$$observable]: observable
    }
}

function applyMiddleware(...middlewares) {
    return createStore => (...args) => {
            const store = createStore(...args)
            let dispatch = () => {
            throw new Error('...')
        }

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

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

        return {
            ...store,
            dispatch
        }
    }
}

Middleware的原型:

interface MiddlewareAPI {
  dispatch: Dispatch
  getState(): any
}

interface Middleware {
  (api: MiddlewareAPI): (next: Dispatch) => (action: Action) => any
}

1.1 CreateStore流程

步骤一:我们通过applyMiddleware(...middlewares)函数将多个中间件包装成一个StoreEnhancer,在createStore时传到第三个参数上,此时先走到分支一enhancer(createStore)(reducer, preloadedState)使用applyMiddleware增强器创建Store。

步骤二:在applyMiddleware返回的函数中,使用入参createStore去创建原始的Store,即走到分支二

步骤三: 在applyMiddleware的结尾处,使用Middlewares重新包装的dispatch替换原始的store.dispatch,各个Middlewares的入参middlewareAPI也会使用新的dispatch

1.2 组合Middlewares机制

组合多个Middlewares的过程,主要在这两行代码:

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

第1行代码:获取到 (next: Dispatch) => (action: Action) => any 原型的函数集合,假设chian = [F1, F2, F3]

第2行代码:从右往左柯里化组合多个中间件dispatch = F1(F_OTHERS_1) = F1(F2(F_OTHERS_2)) = F1(F2(F3(原始store.dispatch)))dispatch的原型为(action: Action) => any

触发Action时,调用dispatch(action)等于:

F1(F_OTHERS_1)(action) // dispatch(action)
    F2(F_OTHERS_2)(action) // next(action) in F1
        F3(原始store.dispatch)(action) // next(action) in F2
           原始store.dispatch(action)  // next(action) in F3
}

可见:在创建store时已经执行第1和2行代码;在最后一个中间件中的next(action)就是调用未增强前的原始store.dispatch(action)。

redux-thunkredux-promise是两个特殊的中间件,它们先对要处理的action进行过滤,只有POD的action对象才向后调用next(action)。

redux-observableredux-saga则先调用next(action),再处理sideEffect事务。

2 redux-log与redux-thunk执行次序样例

export function logger1({ getState }) {
    console.log('logger1: building middleware')
    return (next) => {
        console.log('logger1: currying next')

        return (action) => {
            console.log('logger1: will dispatch', action)

            // 调用 middleware 链中下一个 middleware 的 dispatch。
            let returnValue = next(action)

            console.log('logger1: state after dispatch', getState())

            // 一般会是 action 本身,除非
            // 后面的 middleware 修改了它。
            return returnValue
        }
    }
}

export function logger2({ getState }) {
    console.log('logger2: building middleware')

    return (next) => {
        console.log('logger2: currying next')

        return (action) => {
            console.log('logger2: will dispatch', action)

            // 调用 middleware 链中下一个 middleware 的 dispatch。
            let returnValue = next(action)

            console.log('logger2: state after dispatch', getState())

            // 一般会是 action 本身,除非
            // 后面的 middleware 修改了它。
            return returnValue
        }
    }
}

export function logger3({ getState }) {
    console.log('logger3: building middleware')

    return (next) => {
        console.log('logger3: currying next')

        return (action) => {
            console.log('logger3: will dispatch', action)

            // 调用 middleware 链中下一个 middleware 的 dispatch。
            let returnValue = next(action)

            console.log('logger3: state after dispatch', getState())

            // 一般会是 action 本身,除非
            // 后面的 middleware 修改了它。
            return returnValue
        }
    }
}

export function thunk({ dispatch, getState }) {
    console.log('thunk: building middleware', dispatch, getState)

    return (next) => {
        console.log('thunk: currying next')

        return (action) => {
            console.log('thunk: will dispatch', action)

            if (typeof action === 'function') {
                return action(dispatch, getState);
            }

            let returnValue = next(action)
            console.log('thunk: state after dispatch', getState())
            return returnValue
        }
    };
}

输出:

Reducer counter Object {type: "@@redux/INITx.b.b.s.8.p"}
logger1: building middleware
logger2: building middleware
logger3: building middleware
thunk: building middleware
thunk: currying next
logger3: currying next
logger2: currying next
logger1: currying next
rootSaga begin
rootSaga end
Render on Subscribing

dispatch beginning...
logger1: will dispatch Object {type: "INCREMENT"}
logger2: will dispatch Object {type: "INCREMENT"}
logger3: will dispatch Object {type: "INCREMENT"}
thunk: will dispatch {type: "INCREMENT"}
Reducer counter Object {type: "INCREMENT"}
Reducer INCREMENT
Render on Subscribing

thunk: state after dispatch 1
logger3: state after dispatch 1
logger2: state after dispatch 1
logger1: state after dispatch 1