§ applyMiddleware(…middlewares)
Redux 中文文档 [高级 · Middleware][redux-middleware] 有提到中间件的演化由来
首先要理解何谓 Middleware
,何谓 Enhancer
⊙ Middleware
说白了,Redux 引入中间件机制,其实就是为了在 dispatch
前后,统一“做爱做的事”。。。
诸如统一的日志记录、引入 thunk 统一处理异步 Action Creator 等都属于中间件
下面是一个简单的打印动作前后 state
的中间件:
/* 装逼写法 */
const printStateMiddleware = ({ getState }) => next => action => {
console.log('state before dispatch', getState())
let returnValue = next(action)
console.log('state after dispatch', getState())
return returnValue
}
-------------------------------------------------
/* 降低逼格写法 */
function printStateMiddleware(middlewareAPI) { // 记为【锚点-1】,中间件内可用的 API
return function (dispatch) { // 记为【锚点-2】,传入上级中间件处理逻辑(若无则为原 store.dispatch)
// 下面记为【锚点-3】,整个函数将会被传到下级中间件(如果有的话)作为它的 dispatch 参数
return function (action) { // <---------------------------------------------- 这货就叫做【中间件处理逻辑哥】吧
console.log('state before dispatch', middlewareAPI.getState())
var returnValue = dispatch(action) // 还记得吗,dispatch 的返回值其实还是 action
console.log('state after dispatch', middlewareAPI.getState())
return returnValue // 将 action 返回给上一个中间件(实际上可以返回任意值,或不返回)
// 在此衷心感谢 @zaleGZL 在 issue15 中指出之前我对此处的错误解读
}
}
}
⊙ Store Enhancer
说白了,Store 增强器就是对生成的 store
API 进行改造,这是它与中间件最大的区别(中间件不修改 store
的 API)
而改造 store
的 API 就要从它的缔造者 createStore
入手。例如,Redux 的 API applyMiddleware
就是一个 Store 增强器:
import compose from './compose' // 这货的作用其实就是 compose(f, g, h)(action) => f(g(h(action)))
/* 传入一坨中间件 */
export default function applyMiddleware(...middlewares) {
/* 传入 createStore */
return function(createStore) {
/* 返回一个函数签名跟 createStore 一模一样的函数,亦即返回的是一个增强版的 createStore */
return function(reducer, preloadedState, enhancer) {
// 用原 createStore 先生成一个 store,其包含 getState / dispatch / subscribe / replaceReducer 四个 API
var store = createStore(reducer, preloadedState, enhancer)
var dispatch = store.dispatch // 指向原 dispatch
var chain = [] // 存储中间件的数组
// 提供给中间件的 API(其实都是 store 的 API)
var middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
// 给中间件“装上” API,见上面 ⊙Middleware【降低逼格写法】的【锚点-1】
chain = middlewares.map(middleware => middleware(middlewareAPI))
// 串联所有中间件
dispatch = compose(...chain)(store.dispatch)
// 例如,chain 为 [M3, M2, M1],而 compose 是从右到左进行“包裹”的
// 那么,M1 的 dispatch 参数为 store.dispatch(见【降低逼格写法】的【锚点-2】)
// 往后,M2 的 dispatch 参数为 M1 的中间件处理逻辑哥(见【降低逼格写法】的【锚点-3】)
// 同样,M3 的 dispatch 参数为 M2 的中间件处理逻辑哥
// 最后,我们得到串联后的中间件链:M3(M2(M1(store.dispatch)))
//(这种形式的串联类似于洋葱,可参考文末的拓展阅读:中间件的洋葱模型)
// 在此衷心感谢 @ibufu 在 issue8 中指出之前我对此处的错误解读
return {
...store, // store 的 API 中保留 getState / subsribe / replaceReducer
dispatch // 新 dispatch 覆盖原 dispatch,往后调用 dispatch 就会触发 chain 内的中间件链式串联执行
}
}
}
}
最终返回的虽然还是 store
的那四个 API,但其中的 dispatch
函数的功能被增强了,这就是所谓的 Store Enhancer
⊙ 综合应用 ( [在线演示][jsbin] )
<!DOCTYPE html>
<html>
<head>
<script src="//cdn.bootcss.com/redux/3.5.2/redux.min.js"></script>
</head>
<body>
<script>
/** Action Creators */
function inc() {
return { type: 'INCREMENT' };
}
function dec() {
return { type: 'DECREMENT' };
}
function reducer(state, action) {
state = state || { counter: 0 };
switch (action.type) {
case 'INCREMENT':
return { counter: state.counter + 1 };
case 'DECREMENT':
return { counter: state.counter - 1 };
default:
return state;
}
}
function printStateMiddleware(middlewareAPI) {
return function (dispatch) {
return function (action) {
console.log('dispatch 前:', middlewareAPI.getState());
var returnValue = dispatch(action);
console.log('dispatch 后:', middlewareAPI.getState(), '\n');
return returnValue;
};
};
}
var enhancedCreateStore = Redux.applyMiddleware(printStateMiddleware)(Redux.createStore);
var store = enhancedCreateStore(reducer);
store.dispatch(inc());
store.dispatch(inc());
store.dispatch(dec());
</script>
</body>
</html>
控制台输出:
dispatch 前:{ counter: 0 }
dispatch 后:{ counter: 1 }
dispatch 前:{ counter: 1 }
dispatch 后:{ counter: 2 }
dispatch 前:{ counter: 2 }
dispatch 后:{ counter: 1 }
实际上,上面生成 store
的代码可以更加优雅:
/** 本代码块记为 code-10 **/
var store = Redux.createStore(
reducer,
Redux.applyMiddleware(printStateMiddleware)
)
如果有多个中间件以及多个增强器,还可以这样写(请留意序号顺序):
重温一下
createStore
完整的函数签名:function createStore(reducer, preloadedState, enhancer)
/** 本代码块记为 code-11 **/
import { createStore, applyMiddleware, compose } from 'redux'
const store = createStore(
reducer,
preloadedState, // 可选,前后端同构的数据同步
compose( // 还记得吗?compose 是从右到左的哦!
applyMiddleware( // 这货也是 Store Enhancer 哦!但这是关乎中间件的增强器,必须置于 compose 执行链的最后
middleware1,
middleware2,
middleware3
),
enhancer3,
enhancer2,
enhancer1
)
)
为什么会支持那么多种写法呢?在 createStore
的源码分析的开头部分,我省略了一些代码,现在奉上该压轴部分:
/** 本代码块记为 code-12 **/
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
// 这里就是上面 code-10 的情况,只传入 reducer 和 Store Enhancer 这两个参数
enhancer = preloadedState
preloadedState = undefined
}
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
// 存在 enhancer 就立即执行,返回增强版的 createStore <--------- 记为【锚点 12-1】
return enhancer(createStore)(reducer, preloadedState)
}
if (typeof reducer !== 'function') {
throw new Error('Expected the reducer to be a function.')
}
// 除 compose 外,createStore 竟然也在此为我们提供了书写的便利与自由度,实在是太体贴了
如果像 code-11
那样有多个 enhancer
,则 code-12 【锚点 12-1】
中的代码会执行多次
生成最终的超级增强版 store
。最后,奉上 code-11
中 compose
内部的执行顺序示意图:
原 createStore ————
│
↓
return enhancer1(createStore)(reducer, preloadedState, enhancer2)
|
├———————→ createStore 增强版 1
│
↓
return enhancer2(createStore1)(reducer, preloadedState, enhancer3)
|
├———————————→ createStore 增强版 1+2
│
↓
return enhancer3(createStore1+2)(reducer, preloadedState, applyMiddleware(m1,m2,m3))
|
├————————————————————→ createStore 增强版 1+2+3
│
↓
return appleMiddleware(m1,m2,m3)(createStore1+2+3)(reducer, preloadedState)
|
├——————————————————————————————————→ 生成最终增强版 store