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等类似的东西组合使用了