1、applyMiddleware 介绍
applyMiddleware 作为 Redux 的核心 api 之一,本质就是在 dispatch 更改 reducer 之前做一些操作,具体的实现其实就对 store 的增强,其中最终是对 store 中的 dispatch 的增强。关于 applyMiddleware 的使用可参考上一章节 Redux源码分析(1) - Redux介绍及使用 和 官方文档 applyMiddleware 。
Middleware 可以让你包装 store 的 dispatch 方法来达到你想要的目的。同时, middleware 还拥有“可组合”这一关键特性。多个 middleware 可以被组合到一起使用,形成 middleware 链。其中,每个 middleware 都不需要关心链中它前后的 middleware 的任何信息。
applyMiddleware 的源码结构图如下图所示。
首先看下 applyMiddleware 的使用方式,以当前 demo 为实例:
// middleware
const Logger = (store) => (next) => (action) => {
console.info('logger start');
let result = next(action);
console.info('logger end');
};
const Test = (store) => (next) => (action) => {
console.info('test start');
let result = next(action);
console.info('test end');
};
let store = createStore(rootReducer, applyMiddleware(Logger, Test));
这里涉及到两个 api : createStore 、 applyMiddleware 。 在 createStore 的源码分析中,有提到存在 enhancer 时,直接执行下面的语句。
return enhancer(createStore)(reducer, preloadedState);
配合当前实例可知。此时的 enhancer 就是 applyMiddleware(Logger, Test) 的结果。
2、applyMiddleware 源码分析
applyMiddleware 的源码如下:
import compose from './compose';
export default function applyMiddleware(...middlewares) {
/*
1、中间件的使用方式如下:let store = createStore(rootReducer, applyMiddleware(Logger, Test));
2、在 createStore 方法中,当存在 enhancer 时,执行语句 enhancer(createStore)(reducer, preloadedState);
3、可知 enhancer 即为 applyMiddleware(Logger, Test) 的执行结果,也即下边返回的高阶函数。
*/
return createStore => (...args) => {
/*
1、此处即执行 enhancer 的执行。可知 createStore 参数即为 redux 的 createStore方法, ...agrs 表示 reducer, preloadedState
2、store 即为createStore方法在没有enhancer的时候执行的结果。也即中间件作用前的原始store
*/
const store = createStore(...args);
// 定义了一个dispatch, 调用会 throw new Error(dispatching虽然构造middleware但不允许其他middleware应用 )
let dispatch = () => {
//……
};
// debugger
// 定义middlewareAPI, 传递 getState、 dispatch到中间件。这也是中间件中能访问state的原因。
const middlewareAPI = {
getState: store.getState,
// 重新定义dispatch,此处不是直接赋值,是因为 dispatch 是引用赋值,直接赋值的话,后边更改dispatch会影响到原有的dispatch
dispatch: (...args) => dispatch(...args)
};
/*
1、中间件形式 store => next => action =>{},
2、执行后即返回 [ (next)=>acticon=>{...next(action)...}]的array,赋值给chain
3、以当前demo为实例。及此时的Logger执行后的结果为f1, 记 Test执行后的结果为f2
*/
const chain = middlewares.map(middleware => middleware(middlewareAPI));
/*
1、compose(...funcs)执行后返回 : (...args)=>(funcA(funcB(...args)),其中funcA、func 为 funcs的元素
2、以当前实例说明。则 compose(...chain) 执行的结果 (...args)=>(f1(f2(...args)) ,其中 f1、f2为上边定义的 Logger、Test执行的结果
3、compose(...chain)(store.dispatch) 执行的结果即为 f1(f2(store.dispatch)) ,此时 f2(store.dispatch) 作为了 f1 的next参数
4、由f1的形式可知。此时 dispatch = (action) => {... f2(store.dispatch) (action) ...}, 也即store 中的增强的dispatch
5、当执行 store.dispatch(action)时,实际执行的是 (action) => {... f2(store.dispatch) (action) ...}。
6、则此时 f2 会执行,其中 next参数为store.dispatch,action参数为action。
7、如果有多个中间件。则可知增强的dispatch为:f1(f2(f3(...fn(store.dispatch))))。
8、则f1执行时 next = f2(f3(...fn(store.dispatch))),next(action)即执行 f2(f3(...fn(store.dispatch)))(action)
9、此时即f2执行,此时next = f3(...fn(store.dispatch)),依次类推到最后next为 store.dispatch。
10、由此也可知,中间件的函数中必须有next(action)语句的执行。
*/
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch // 返回增强的store
};
};
}
applyMiddleware 的源码分析,注释部分还算详细。applyMiddleware 作用中间件之后返回一个高阶函数,由源码可知这个高阶函数形式如下:
const enhancer = createStore => (...args) => { ....... } //语句(1)
enhancer 作为参数传入 createStore。当 createStore 方法中存在 enhancer 时会直接执行下边语句:
return enhancer(createStore)(reducer, preloadedState); // 语句(2)
此时实际执行的高阶函数就语句(1)的执行,其中传入的参数依次是 createStore (redux 的 createStore 方法)和 reducer、 preloadedState。
applyMiddleware逻辑都在返回的高阶函数中, 主要干了以下几件事:
2.1、 获取原始store
const store = createStore(...args);
由上面的分析可知,createStore 即为 redux 的 createStore 方法, ...args 是 reducer 、preloadedState。所以这句话可以理解:在没有中间件时,通过当前 reducer 创建的 store。在之前的章节中有提到过,中间件本质就是对 store 的增强 ,此处先拿到原始的 store 正是为了后边的增强做准备的。
2.2、中间件初始化
这里叫中间件初始化,也不是很准确,可以理解为中间件的执行,因为中间件是一个高阶函数,此处定义为中间件的第一次执行,可与上文中的源码结构图对照。其对应的代码如下:
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
};
const chain = middlewares.map(middleware => middleware(middlewareAPI));
middlewareAPI 中包含 getState 和 dispatch 属性, middlewares 为传入的中间件列表,通过map执行后,返回的是形如:(next)=>acticon=>{...next(action)...} 的一个数组。以当前demo为实例,分别执行 Logger、 Test,返回结果分别标记为 f1、 f2 (为了方便阐述)
let f1 = next => action =>{
// Logger
next.action()
}
let f2 = next => action =>{
// Test
next.action()
}
chain = [f1, f2]
2.3、中间件组合
export default function compose(...funcs) {
// ..... 边界的判断
const a = funcs.reduce((a, b) => {
return (...args) => a(b(...args));
});
return a;
}
先来看下 compose 方法的源码。通过数组的 reduce 方法循环执行,实现函数的组合,最终返回一个复合函数。
compose(func1, func2, func3, ... ,funcn) 返回 (...args) => f1(f2(f3(....fn(args))))
2.4、 增强dispatch,并返回最新的store
dispatch = compose(...chain)(store.dispatch)
这句话是整个 applyMiddleware 代码的关键。根据2.3的分析可知 compose(...chain) 执行的结果就是所有中间件函数第一执行(参数为store)后的组合。
有上文可知: f1、f2 ...... fn 都是中间件第一执行之后返回的高阶函数 :(next) => acticon => { ...next(action)...}
compose(...chain)(store.dispatch) 执行的结果可以理解为 f1(f2(f3(....fn(store.dispatch)))的执行,此时 f1 的入参 next = f2(f3(....fn(store.dispatch)) ,执行后赋值 dispatch
dispatch = action => {
//...... before
f2(f3(....fn(store.dispatch))(action);
//...... after
}
组合后将 store.dispatch 作为组合函数的入参,并返回一个新的 dispatch 函数,覆盖 store 中原有的 dispatch 属性,这既保持了与 createStore 输出结果的一致性;也实现了 dispatch 的增强,到这里也解释 applyMiddleware 本质上是对 dispatch 的增强。
当执行 store.dispatch(action)时,此时的 dispatch 就是被增强的 dispatch,继续分析
根据上边分析,此时的dispatch为:
dispatch = action => {
//...... before // (第1个中间件的before)
f2(f3(....fn(store.dispatch)(action);
//...... after // (第1个中间件的after)
}
执行 store.dispatch(action)后,
(1)首先执行:(第1个中间件的before),
(2)继而执行 f2(f3(....fn(store.dispatch))(action),此时 f2 函数的 next = f3(....fn(store.dispatch)。
f2 = next => action =>{
//...... before // (第2个中间件的before)
next(action)
//...... after // (第2个中间件的after)
}
(3)执行: (第2个中间件的before)
(4)继而执行 next(action),也即执行 f3(....fn(store.dispatch)(action)
(5) 以此类推:f1 ---- next = f2(f3(... fn(store.dispatch)))
f2 ---- next = f3(... fn(store.dispatch))
f3 ---- next = f4(... fn(store.dispatch))
fn ---- next = store.dispatch
......
(6) 执行:(第2个中间件的after)
(7) 执行:(第1个中间件的after)
以上部分的分析,也符合 Redux 官文文档中,关于中间件的介绍。每个中间件中执行 next (action) 是保证中间件能够形成 middleware 链 的关键。
...middlewares (arguments): 遵循 Redux middleware API 的函数。每个 middleware 接受 Store 的 dispatch 和 getState 函数作为命名参数,并返回一个函数。该函数会被传入 被称为 next 的下一个 middleware 的 dispatch 方法,并返回一个接收 action 的新函数,这个函数可以直接调用 next(action),或者在其他需要的时刻调用,甚至根本不去调用它。调用链中最后一个 middleware 会接受真实的 store 的 dispatch 方法作为 next 参数,并借此结束调用链。所以,middleware 的函数签名是 ({ getState, dispatch }) => next => action
2.5、 洋葱模型
通过上文的分析可知,中间件是在 dispatch 更改 reducer 之前做一些动作,根据 2.4 的可知,增强的 dispatch 在执行过程中,实际是:
其执行过程对应的就是洋葱模型,先从外到里层层深入直到最里层,然后从最里层层往外,直到最外层。
结合本文中实例。可知最后中间件 dispatch 时输出的结果如下:
logger start
test start
test end
logger end
3、小结
- 中间件本质对是 store 的增强,准确来说是最 store 中的 dispatch 的增强;
- 中间件能够形成组合链,依赖于 next(action) 的层层传递;
- 中间件的调用符合洋葱模型,由外到里再由里到外。