redux源码 | Part2_ middleware

学习资料

redux源码 v 4.x

学习目标

  1. redux源码createStore(Part1)
  2. redux中间件原理(Part2)
  3. redux源码combineReducers(Part3)

理解Middleware 中间件

Middleware 中间件

看了官方文档的两个例子:日志和异常监控,对中间件的妙用有了初步的理解

middleware洋葱模型

过程解析

compose

compose函数: 将多个函数按照顺序执行,前一个函数的返回值作为下一个函数的参数,最终返回结果

乍一看,哎呀妈呀,这compose函数也太亲切了吧![开心出东北话~]

1
2
3
4
5
6
7
8
9
10
11
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}

if (funcs.length === 1) {
return funcs[0]
}

return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

compose函数妙用:

1
2
3
4
5
6
7
8
9
10
11
12
13
const funcs = [a, b, c];

function a(x) {
return x+2;
};
function b(x) {
return x+3;
};
function c(x) {
return x+10;
};

console.log(compose(...funcs)(3)) // 18 = 3 + 2 + 3 + 10

middleware

applyMiddleware函数:这个函数主要是返回一个createStore函数,其中createStore函数的返回值之一的dispatch是经过中间件包装的

所以这个函数我们主要关注dispatch是怎么被包装的~~

输入参数说明:

​ - middlewares 多个中间件

输出说明:

​ - createStore函数

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
import compose from './compose'

export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
// 创建一个store
const store = createStore(...args)
let dispatch = () => {
throw new Error(
'Dispatching while constructing your middleware is not allowed. ' +
'Other middleware would not be applied to this dispatch.'
)
}

// 下面的代码主要是对dispatch进行包装

/*
middlewareAPI是一个对象,包含getState方法和dispatch方法(起到包装的作用)
*/
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args) // 闭包
}
/*
我们知道,多个middlewares是采用链式调用的解构的
首先把每个middleware都包装成一个函数
然后很好地用到了compose函数,对这些中间件进行嵌套执行,初始参数为store.dispatch
store.dispatch --M1--> res1 --M2--> res2 --M3--> ... --M N--> final res
*/
// map: middleware -> middleware(middlewareAPI),相当于返回的是middleware(middlewareAPI)一次调用后的结果
// 具体看example_redux_thunk里的{dispatch, getState} => next => action => {}这个部分
// 所以chain相当于是
/*
[
(next) => (action) => {}, ---M1
(next) => (action) => {}, ---M2
(next) => (action) => {}, ---M3
...
]

经过compose之后
dispath等于
M2(M1(M3(stroe.dispatch)))的结果

[这一步就是利用洋葱模型一步一步再包装dispatch]

代入洋葱模型想一想是不是特别容易理解?

*/
const chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch) // 利用中间件一层层包装dispatch,生成一个新的dispatch

return {
...store, // 对象解构
dispatch // 同名属性覆盖
}
}
}

example_redux_thunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function createThunkMiddle(extraArgument) {
return ({dispatch, getState} => (next) => (action) => { // 典型的柯里化函数
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument); // 洋葱模型可以提前返回
}

return next(action);
})
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddle;

export default thunk;