§ applyMiddleware(…middlewares)

Redux 中文文档 [高级 · Middleware][redux-middleware] 有提到中间件的演化由来

首先要理解何谓 Middleware,何谓 Enhancer

⊙ Middleware

说白了,Redux 引入中间件机制,其实就是为了在 dispatch 前后,统一“做爱做的事”。。。
诸如统一的日志记录、引入 thunk 统一处理异步 Action Creator 等都属于中间件
下面是一个简单的打印动作前后 state 的中间件:

  1. /* 装逼写法 */
  2. const printStateMiddleware = ({ getState }) => next => action => {
  3. console.log('state before dispatch', getState())
  4. let returnValue = next(action)
  5. console.log('state after dispatch', getState())
  6. return returnValue
  7. }
  8. -------------------------------------------------
  9. /* 降低逼格写法 */
  10. function printStateMiddleware(middlewareAPI) { // 记为【锚点-1】,中间件内可用的 API
  11. return function (dispatch) { // 记为【锚点-2】,传入上级中间件处理逻辑(若无则为原 store.dispatch)
  12. // 下面记为【锚点-3】,整个函数将会被传到下级中间件(如果有的话)作为它的 dispatch 参数
  13. return function (action) { // <---------------------------------------------- 这货就叫做【中间件处理逻辑哥】吧
  14. console.log('state before dispatch', middlewareAPI.getState())
  15. var returnValue = dispatch(action) // 还记得吗,dispatch 的返回值其实还是 action
  16. console.log('state after dispatch', middlewareAPI.getState())
  17. return returnValue // 将 action 返回给上一个中间件(实际上可以返回任意值,或不返回)
  18. // 在此衷心感谢 @zaleGZL 在 issue15 中指出之前我对此处的错误解读
  19. }
  20. }
  21. }

⊙ Store Enhancer

说白了,Store 增强器就是对生成的 store API 进行改造,这是它与中间件最大的区别(中间件不修改 store 的 API)
而改造 store 的 API 就要从它的缔造者 createStore 入手。例如,Redux 的 API applyMiddleware 就是一个 Store 增强器:

  1. import compose from './compose' // 这货的作用其实就是 compose(f, g, h)(action) => f(g(h(action)))
  2. /* 传入一坨中间件 */
  3. export default function applyMiddleware(...middlewares) {
  4. /* 传入 createStore */
  5. return function(createStore) {
  6. /* 返回一个函数签名跟 createStore 一模一样的函数,亦即返回的是一个增强版的 createStore */
  7. return function(reducer, preloadedState, enhancer) {
  8. // 用原 createStore 先生成一个 store,其包含 getState / dispatch / subscribe / replaceReducer 四个 API
  9. var store = createStore(reducer, preloadedState, enhancer)
  10. var dispatch = store.dispatch // 指向原 dispatch
  11. var chain = [] // 存储中间件的数组
  12. // 提供给中间件的 API(其实都是 store 的 API)
  13. var middlewareAPI = {
  14. getState: store.getState,
  15. dispatch: (action) => dispatch(action)
  16. }
  17. // 给中间件“装上” API,见上面 ⊙Middleware【降低逼格写法】的【锚点-1】
  18. chain = middlewares.map(middleware => middleware(middlewareAPI))
  19. // 串联所有中间件
  20. dispatch = compose(...chain)(store.dispatch)
  21. // 例如,chain 为 [M3, M2, M1],而 compose 是从右到左进行“包裹”的
  22. // 那么,M1 的 dispatch 参数为 store.dispatch(见【降低逼格写法】的【锚点-2】)
  23. // 往后,M2 的 dispatch 参数为 M1 的中间件处理逻辑哥(见【降低逼格写法】的【锚点-3】)
  24. // 同样,M3 的 dispatch 参数为 M2 的中间件处理逻辑哥
  25. // 最后,我们得到串联后的中间件链:M3(M2(M1(store.dispatch)))
  26. //(这种形式的串联类似于洋葱,可参考文末的拓展阅读:中间件的洋葱模型)
  27. // 在此衷心感谢 @ibufu 在 issue8 中指出之前我对此处的错误解读
  28. return {
  29. ...store, // store 的 API 中保留 getState / subsribe / replaceReducer
  30. dispatch // 新 dispatch 覆盖原 dispatch,往后调用 dispatch 就会触发 chain 内的中间件链式串联执行
  31. }
  32. }
  33. }
  34. }

最终返回的虽然还是 store 的那四个 API,但其中的 dispatch 函数的功能被增强了,这就是所谓的 Store Enhancer

⊙ 综合应用 ( [在线演示][jsbin] )

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <script src="//cdn.bootcss.com/redux/3.5.2/redux.min.js"></script>
  5. </head>
  6. <body>
  7. <script>
  8. /** Action Creators */
  9. function inc() {
  10. return { type: 'INCREMENT' };
  11. }
  12. function dec() {
  13. return { type: 'DECREMENT' };
  14. }
  15. function reducer(state, action) {
  16. state = state || { counter: 0 };
  17. switch (action.type) {
  18. case 'INCREMENT':
  19. return { counter: state.counter + 1 };
  20. case 'DECREMENT':
  21. return { counter: state.counter - 1 };
  22. default:
  23. return state;
  24. }
  25. }
  26. function printStateMiddleware(middlewareAPI) {
  27. return function (dispatch) {
  28. return function (action) {
  29. console.log('dispatch 前:', middlewareAPI.getState());
  30. var returnValue = dispatch(action);
  31. console.log('dispatch 后:', middlewareAPI.getState(), '\n');
  32. return returnValue;
  33. };
  34. };
  35. }
  36. var enhancedCreateStore = Redux.applyMiddleware(printStateMiddleware)(Redux.createStore);
  37. var store = enhancedCreateStore(reducer);
  38. store.dispatch(inc());
  39. store.dispatch(inc());
  40. store.dispatch(dec());
  41. </script>
  42. </body>
  43. </html>

控制台输出:

  1. dispatch 前:{ counter: 0 }
  2. dispatch 后:{ counter: 1 }
  3. dispatch 前:{ counter: 1 }
  4. dispatch 后:{ counter: 2 }
  5. dispatch 前:{ counter: 2 }
  6. dispatch 后:{ counter: 1 }

实际上,上面生成 store 的代码可以更加优雅:

  1. /** 本代码块记为 code-10 **/
  2. var store = Redux.createStore(
  3. reducer,
  4. Redux.applyMiddleware(printStateMiddleware)
  5. )

如果有多个中间件以及多个增强器,还可以这样写(请留意序号顺序):

重温一下 createStore 完整的函数签名:function createStore(reducer, preloadedState, enhancer)

  1. /** 本代码块记为 code-11 **/
  2. import { createStore, applyMiddleware, compose } from 'redux'
  3. const store = createStore(
  4. reducer,
  5. preloadedState, // 可选,前后端同构的数据同步
  6. compose( // 还记得吗?compose 是从右到左的哦!
  7. applyMiddleware( // 这货也是 Store Enhancer 哦!但这是关乎中间件的增强器,必须置于 compose 执行链的最后
  8. middleware1,
  9. middleware2,
  10. middleware3
  11. ),
  12. enhancer3,
  13. enhancer2,
  14. enhancer1
  15. )
  16. )

为什么会支持那么多种写法呢?在 createStore 的源码分析的开头部分,我省略了一些代码,现在奉上该压轴部分:

  1. /** 本代码块记为 code-12 **/
  2. if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
  3. // 这里就是上面 code-10 的情况,只传入 reducer 和 Store Enhancer 这两个参数
  4. enhancer = preloadedState
  5. preloadedState = undefined
  6. }
  7. if (typeof enhancer !== 'undefined') {
  8. if (typeof enhancer !== 'function') {
  9. throw new Error('Expected the enhancer to be a function.')
  10. }
  11. // 存在 enhancer 就立即执行,返回增强版的 createStore <--------- 记为【锚点 12-1】
  12. return enhancer(createStore)(reducer, preloadedState)
  13. }
  14. if (typeof reducer !== 'function') {
  15. throw new Error('Expected the reducer to be a function.')
  16. }
  17. // 除 compose 外,createStore 竟然也在此为我们提供了书写的便利与自由度,实在是太体贴了

如果像 code-11 那样有多个 enhancer,则 code-12 【锚点 12-1】 中的代码会执行多次
生成最终的超级增强版 store。最后,奉上 code-11compose 内部的执行顺序示意图:

  1. createStore ————
  2. return enhancer1(createStore)(reducer, preloadedState, enhancer2)
  3. |
  4. ├———————→ createStore 增强版 1
  5. return enhancer2(createStore1)(reducer, preloadedState, enhancer3)
  6. |
  7. ├———————————→ createStore 增强版 1+2
  8. return enhancer3(createStore1+2)(reducer, preloadedState, applyMiddleware(m1,m2,m3))
  9. |
  10. ├————————————————————→ createStore 增强版 1+2+3
  11. return appleMiddleware(m1,m2,m3)(createStore1+2+3)(reducer, preloadedState)
  12. |
  13. ├——————————————————————————————————→ 生成最终增强版 store