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-thunk
与redux-promise
是两个特殊的中间件,它们先对要处理的action进行过滤,只有POD的action对象才向后调用next(action)。
redux-observable
与redux-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