Redux源码解读
早就想找个时间看看react全家桶的源码了 这次先从redux看起,后续还会有react-redux和react-router-redux
下面看代码
目录结构如下:
- applyMiddleware.js
- bindActionCreator.js
- combineReducers.js
- compose.js
- createStore.js
- index.js
先看入口文件
index.js
入口文件主要是暴露1-5的核心方法 代码就不贴了
createStore.js
1 | // reducer是一个函数, 接收一个单独的reducer或者经过combineRedeucers组合的多个reducer |
下面我们看看combineReducers做了什么
combineReducers.js
1 | // 看这个文件名字就知道是一个组合多个reducer的方法 |
看完以上代码我们先举个小例子介绍一下enhancer到底是何方神圣
- touch index.js
- 开始写demo
1 | const { createStore, combineReducers, applyMiddleware, compose } = require('redux') |
接着我们看看compose和applyMiddleware做了什么吧~
compose.js
1 | export default function compose(...funcs) { |
compose的代码很少 通过reduce方法将各中间件形成一个执行链 前一个中间件套在后一个的外层 并接受后一个函数的执行结果作为自己的入参
举个栗子吧1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16const a = function () {}
const b = function () {}
const c = function () {}
compose([a, b, c])
// 先处理a和b 返回
const ab = function (...args) {
return a(b(...args))
}
// 再处理ab和c 返回
const abc = function (...args) {
return ab(c(...args))
// 这个c(...args)执行结果就是ab的入参
// 所以我们可以换个写法
return a(b(c(...args)))
}
// 换而言之compose的返回值就是 (...args) => a(b(c(...args)))
applyMiddleware.js
我们看看applyMiddleware是怎么实现的 找找他是怎么通过compose组合成enhancer的
1 | // 看到这段代码 我觉得猜测是对的 是不是有点像上面的loggerEnhancer的结构 重写dispatch方法 |
我们再写个使用applyMiddleware小demo 通过demo我们再进行设想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
69
70
71
72
73
74
75
76
77
78const { createStore, applyMiddleware, compose } = require('redux')
const initialState = {
count: 1,
}
function count1(state = initialState, action) {
switch (action.type) {
case 'ADD':
return {
...state,
count: state.count + 1,
}
default:
return state
}
}
const store = createStore(count1, applyMiddleware(fn1, fn2))
// 上面两个fn1和fn2我们都没定义 现在我们根据applyMiddleware猜测一下fn1和fn2是啥样子的
// 根据loggerEnhancer的设想
const enhancer = createStore => (reducers, preloadedState, enhancer) => {}
// applyMiddleware也应该是这样的函数体
const chain = middlewares.map(middleware => middleware(middlewareAPI))
// 这行代码可以看出 fn1的雏形 应该是这样的
const fn1 = ({ dispatch, getState }) => {}
// 但是通过compose方法还能接受store.dispatch作为入参 说明fn1应该是这样的
const fn1 = ({ dispatch, getState }) => dispatch => {}
// 通过返回的是一个dispatch 我可以知道fn1原来是这样的
const fn1 = ({ dispatch, getState }) => dispatch => action => {}
// 举个栗子
const a = dispatch => action => {}
const b = dispatch => action => {}
// compose([a, b])的结果为 (...args) => a(b(...args))
// compose([a, b])(store.dispatch) 结果为 a(b(dispatch)) 再做简化就是a(action => {})
// 从这里可以看出 a执行的dispatch其实就是 b(dispatch)的结果
// 如果还有c c执行dispatch就是a(dispatch) 从而达到中间件的作用
// a执行的dispatch就是dispatch
// 举个更确切的栗子
const fn1 = store => next => {
console.log('next1')
return action => {
console.log('fn1')
next(action)
console.log('fn1')
}
}
const fn2 = store => next => {
console.log('next2')
return action => {
console.log('fn2')
next(action)
console.log('fn2')
}
}
/*
* 在执行applyMiddleware时 执行了compose(...chain)(store.sidpatch) 执行顺序是 next2 、 next1 这是中间件入栈的顺序
* 但是调用的顺序不一样 有兴趣可以了解一下洋葱模型
* 执行store.dispatch(action1) 实际打印顺序是 fn1、fn2、fn2、fn1
* fn2在传入store.dispatch的时候 会将
*/
const dispatchFromStore = action => {
console.log('fn2')
next(action)
console.log('fn2')
}
/*
* 当做返回值传给fn1的next
* 那fn1的执行过程就是
* */
const dispatchFromFn2 = action => {
console.log('fn1')
dispatchFromStore(action)
console.log('fn1')
}
// 所以打印顺序是fn1、fn2、fn2、fn1就不奇怪了
bindActionCreators.js
这个文件没有被主函数用到 是被暴露给开发者使用的一个工具函数 用来更方便的组合你的action
createStore/observer方法
这个方法上面没有讲到 也是暴露给开发者的API 我们放到以后讲
小结
这一遍过下来基本就能熟练使用redux的大部分特性了 也感叹代码的精妙 尤其是组合中间件的代码 nb~