redux源码分析:applyMiddleware
redux 添加中间件会调用 applyMiddleware
一个简单的 log
中间件的实现(./middlewares/log.js
)
export default (store) => {
return (next) => {
return (action) => {
console.log("dispatch", action.type);
let result = next(action);
console.log("newState", store.getState());
return result;
};
};
};
先看入口部分(app.js
)
import logMiddleware from './middlewares/log';
//...
createStore(reducer, 10, applyMiddleware(logMiddleware))
源码部分 node_modules/redux/createStore.js
export default function createStore(reducer, preloadedState, enhancer) {
// 省略号...
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
return enhancer(createStore)(reducer, preloadedState)
}
applyMiddleware 做了什么事情(参考了这里)
applyMiddleware(thunk)
就相当于一个enhancer
,它负责扩展redux
,说白了就是扩展store
的dispatch
方法。- 既然要改造 store,那么就得把store作为参数传递进这个enhancer中,再吐出一个改造好的store。吐出来的这个store的dispatch方法,是enhancer改造store的最终实现目标。
export default function applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState, enhancer) => {
const store = createStore(reducer, preloadedState, enhancer)
let dispatch = store.dispatch
let chain = []
const middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
原理解析(改造 createStore)
干的事情: 将用户的store利用中间件进行2次改造本质上改造的东西是:dispatch方法,以及dispatch里面的内容
上面是大多数网上的结论,我有了更加明确的答案:
applyMiddleware
本质上是改造createStore
这个方法- 真正的
middleware
是改造dispatch
方法,网上大部分的解释问题就在这里,把applyMiddleware
方法的作用变成了middleware
的作用了- 这也印证了为什么
createStore
的第3个参数名不叫:middleware
,而叫enhancer
,本质上是createStore
的增强,或者说decorator
- enhancer 其实也是可以组合的,combinedEnhancers 这个方法就有在官方 issue 里讨论过
export default function applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState, enhancer) => {
const store = createStore(reducer, preloadedState, enhancer)
const newDispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch: newDispatch
}
}
}
// 如果只考虑 middleware 机制,这样就行了; 我在实际项目中改造用过,一点问题没有用
export default function applyMiddleware(...middlewares) {
return (store) => {
const newDispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch: newDispatch
}
}
}
关于 enhancer 的一段有趣的讨论
enhancer function was invoked inside createStore:
redux/src/createStore.js line 50: return enhancer(createStore)(reducer, preloadedState).
redux/src/applyMiddleware.js line 20: return (createStore) => (reducer, preloadedState, enhancer) => {
redux/src/applyMiddleware.js line 21: var store = createStore(reducer, preloadedState, enhancer)
...}
// 原始代码
return function (createStore) {
return function (reducer, preloadedState, enhancer) {
var store = createStore(reducer, preloadedState, enhancer);
// 期望的代码
return function(store) {
- 这段代码本质上还是要被类似于
compose
的东西消费 - 所以,只能是这种
currying
的代码,因为可以延时执行 - 直接写成
store, enhancer
就不能继续被compose/combinedEnhancers
等类似的东西组合使用了