0%

Redux中间件原理

Redux中间件原理

Redux中间件

中间件主要被用于分离那些不属于你应用的核心业务逻辑的可被组合起来使用的代码。

  • 不使用middleware时,在dispatch(action)时会执行Reducer,并根据action的type更新返回相应的state。
  • 而在使用middleware时,简言之,middleware会将我们当前的action做相应的处理,随后再交付Reducer执行
  • 通过重写dispatch方法,在派发action之前或之后添加逻辑(AOP)。 你可以利用 Redux middleware 来进行日志记录、创建崩溃报告、调用异步接口或者路由等等。每一个 middleware 处理一个相对独立的业务需求,通过串联不同的 middleware,实现变化多样的的功能。

相关源码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import { createStore } from 'redux';
import reducer from './reducers';
const logger = function ({ getState, dispatch }) {
return function (next) {
return function (action) {
console.log(`旧状态:${JSON.stringify(getState())}`);
next(action);
console.log(`新状态:${JSON.stringify(getState())}`);
}
}
}
const thunk = function ({ getState, dispatch }) {
return function (next) {
return function (action) {
if (typeof action === 'function') {
action(dispatch);
} else {
console.log(next)
next(action)
}
}
}
}
function applyMiddleware(...middlewares) {
return function (createStore) {
return function (...args) {
let store = createStore(...args);//创建store
let dispatch;
//中间件api
let middlewareAPI = {
getState: store.getState,//获取仓库中的状态
//dispatch指向箭头函数会指向包装后的dispatch 即compose(...chain)(store.dispatch) 这样就可以解决多层嵌套派发action的问题
dispatch: (...args) => dispatch(...args)//派发动作
};
const chain = middlewares.map(middleware => middleware(middlewareAPI));
//compose 是函数式编程中的组合,compose 将 chain 中的所有匿名函数,[f1, f2, ... , fx, ..., fn],组装成一个新的函数,即新的 dispatch,当新 dispatch 执行时,[f1, f2, ... , fx, ..., fn],从右到左依次执行( 所以顺序很重要)
dispatch = compose(...chain)(store.dispatch);
//返回重写dispatch方法后的store
return {
...store,
dispatch
};
}
}
}

/**
* reduce有四个参数
* previousValue: 上一次调用callback时返回的值
* currentValue: 正在处理的数组元素
* index: 正在处理的数组元素下标
* array: 被处理的数组
*/
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg;
}

if (funcs.length === 1) {
return funcs[0];
}
//将 funcs 中的所有匿名函数,[f1, f2, ... , fx, ..., fn],组装成一个新的函数,当新的函数执行时,[f1, f2, ... , fx, ..., fn],从右到左依次执行( 所以顺序很重要)
return funcs.reduce((a, b) => (...args) => a(b(...args)));
}

let store = applyMiddleware(thunk, logger)(createStore)(reducer);
// let store = createStore(reducer, applyMiddleware(thunk, logger));
export default store;

代码执行过程分析

  • 1、依次执行middleware:将middleware执行后返回的函数合并到一个chain数组,如下

    • const chain = middlewares.map(middleware => middleware(middlewareAPI))

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      // ES6箭头函数
      export default ({ dispatch, getState }) => next => action => {}

      // 即
      function ({ dispatch, getState }) {
      return function(next) {
      return function (action) {
      return {}
      }
      }
      }

      那么此时合并的chain结构如下

      1
      2
      3
      4
      5
      6
      7
      [    ...,
      function(next) {
      return function (action) {
      return {}
      }
      }
      ]
  • 2、修改dispatch,增加功能:compose函数,实际就是一个柯里化函数,即将所有的middleware合并成一个middleware,并在最后一个middleware中传入当前的dispatch

    • dispatch = compose(…chain)(store.dispatch) 返回重写的dispatch
      compose

    执行 compose(…chain)(store.dispatch),当前 a 对应于 蓝色框 代码,b 对应于 红色框 代码 。首先会执行 红色框 对应的代码,执行后返回 黄色框 对应的代码。此时,蓝色框 中的next指向 黄色框 对应的代码。最终 蓝色框 对应的代码执行,返回 绿色框 的代码。就是重写的 dispatch 函数。

  • 3、触发异步action,执行过程如下

    1
    2
    3
    4
    5
    6
    7
    8
    asyncAdd(){
    //在redux中派发的动作只能是纯对象,并不能 store.dispatch 函数
    return function(dispatch){
    setTimeout(() => {
    dispatch({type:types.ADD});
    }, 1000);
    }
    },

    触发 asyncAdd,执行 绿色框* 的代码,由于当前 action 为 asyncAdd 函数中返回的函数,所有会执行 action(dispatch)。执行asyncAdd 中的函数,执行setTimeout,1秒钟之后执行 dispatch({type:types.ADD}); ,即执行 绿色框* 的代码。由于当前 action 为 对象,所有会执行 else 条件中的代码。当前,next 指向 黄色框 的代码。执行 黄色框 的代码,先打印旧状态,然后执行 next(action);,当前 next 指向原生的 dispatch 函数,会触发 action 修改 state,触发视图的更新。最后打印新状态。

参考

-------------本文结束感谢您的阅读-------------