Redux createStore源码学习

  在react中管理数据状态,必然会用到redux,那么redux是怎么样做到这个状态的转换的,下面我们一起来学习一下吧。

Redux API 和 Redux 源码结构

Redux API

1
2
3
4
5
6
7
export {
createStore, //创建一个state用来存储状态树
combineReducers, //合并reducer
bindActionCreators, //将dispatch和action结合
applyMiddleware, //调度中间件来增强store,例如中间件redux-thunk等
compose //从右向左组合多个函数, compose(f, g, h)会返回(...args) => f(g(h(...args)))
}

源码结构

Redux源码结构和提供的API大体对应,如下:
utils——warning.js //console中打印warning信息要用到的
applyMiddleware.js
bindActionCreators.js
combineReducers.js
compose.js
createStore.js
index.js //export 上述定义的module

createStore

上面我们看到了redux的API和源码结构,看的出来,warning.js和index.js不用解析,都看得懂,关键时其余的几个module,那我们从最重要的createStore讲起。

1
2
3
4
5
export var ActionTypes = {
INIT: '@@redux/INIT'
}
//首先定义了一个action类型,我们知道更新state的唯一方法就是dispatch一个action,这个action是用
// 来初始化state的,后面会用到它

现在来看下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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
//接收三个参数
//reducer为function。当dispatch一个action时,此函数接收action来更新state
//preloadState初始化State
//enhancer 为function。用来增强store, Redux 定义有applyMiddleware来增强store,后面会
//单独讲applyMiddleware
export default function createStore(reducer, preloadedState, enhancer) {
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
//如果只传了两个参数,并且第二个参数为函数,第二个参数会被当作enhancer
enhancer = preloadedState
preloadedState = undefined
}
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
//校验enhancer是否为函数,如果不是函数则抛出异常
throw new Error('Expected the enhancer to be a function.')
}
//如果enhancer存在且为函数,那么则返回如下调用,如果enhancer为applyMiddleware,那么调用则
//是applyMiddleware(middleware1, middleware2, middleware3...)(createStore)(reducer, preloadedState)。后面讲applyMiddleware再详细讲。
return enhancer(createStore)(reducer, preloadedState)
}
if (typeof reducer !== 'function') {
//校验reducer是否为函数
throw new Error('Expected the reducer to be a function.')
}
var currentReducer = reducer
//得到reducer
var currentState = preloadedState
//得到初始init,没有传递则为undefined
var currentListeners = []
//定义一个数组用来存放listeners。就是一个函数数组,当state发生改变时,会循环执行这个数组里面的函数
var nextListeners = currentListeners
//用来存储下一次的listeners数组。为什么要有这个listeners数组呢?因为当state发生改变时,我们根据
//上面的currentListeners来循环执行函数,但是在这执行这些函数时,函数内部可能取消或者添加订阅
//(state改变时,添加或者取消执行函数),这时如果直接操作currentListeners ,相当于在循环
//内部修改循环条件,执行瞬间就乱套了,有没有啊,有没有
var isDispatching = false
//reducer函数是否正在执行的标识
function ensureCanMutateNextListeners() {
//拷贝currentListeners一份为nextListeners,这样nextListeners的改变不会引起currentListeners的改变
//(上面解释过原因啦)
}
function dispatch() {
//触发action去执行reducer,更新state
.....
}
function subscribe() {
//接收一个函数参数,订阅state的改变。当state改变时会执行这个函数
....
}
function getState() {
//获取state树
....
}
function replaceReducer() {
//替换reducer
....
}
function observable() {
//没用,不解释(后面有解释)
......
}
dispatch({ type: ActionTypes.INIT })
//执行dispatch函数,初始化state
return {
//真正的返回,执行createStore其实返回的就是这些东东
dispatch, //触发action去执行reducer,更新state
subscribe, //订阅state改变,state改变时会执行subscribe的参数(自己定义的一个函数)
getState, //获取state树
replaceReducer, //替换reducer
[$$observable]: observable
//redux内部用的,对我们来说没用(非要深究它写这是干嘛的?咋跟
//我一样死脑筋呢,都说了没用啦。算了,还是告诉你把,就是内部用的,在测试代码中会用到,感兴
//趣的可以去test目录下查看)
}
}

现在是不是感觉明朗(懵逼)了许多,懵逼就对了,接下来我们再来解析一下dispatch, subscribe等函数的具体实现,或许会让你明朗(更懵逼)起来
看了上面的大体结构,我们明白以下这些就够了。
createStore是一个函数,它定义了一些变量(currentState, currentListeners等)及函数(dispatch, subscribe等),并且调用了dispatch,最后返回一个对象,该对象包含的就是dispatch和subscribe等函数。接下来我们来解析这些函数。
createStore里面只调用了一个函数,那就是dispatch,那我们就从这里开始讲起。

dispatch

它是这样被调用的,有没有很熟悉。。。

1
dispatch({ type: ActionTypes.INIT })

来看看dispatch的源码

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
function dispatch(action) {
//校验action参数,必须为一个纯粹的对象。这也说明了,我们不能直接在redux做异步请求,而是需要
//使用applyMiddleware去应用一些中间件。比如redux-thunk等
if (!isPlainObject(action)) {
throw new Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.'
)
}
//同样是校验参数,不解释,相信都能看懂
if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. ' +
'Have you misspelled a constant?'
)
}
//判断是否正在执行reducers函数,如果正在执行,此action不会触发,那有人就问题了,那是不是我
//的dispatch不会起作用,导致state没有更新,数据是错误的? 答案是state是不会有错。因为redux本
//身整个更新state的过程是同步的,从dipatch——>reducers——>state。所以这段代码意在你定义的
//reducers函数中不允许调用dispatch
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
}
try {
//设置标识为,并且执行currentReducer,还记得吗?这个我们通过参数获取到的一个函数,往往是我
//们调用combineReducers返回,combineReducers我们后面解析
isDispatching = true
//调用reducers函数
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
//调用所有订阅state改变的函数,这些函数就可以通过getState函数获取到最新的state值。订阅的函数
//从哪里来呢,从subscribe中来, 我们后面来解析subscribe和getState
var listeners = currentListeners = nextListeners
for (var i = 0; i < listeners.length; i++) {
listeners[i]()
}
return action
}

坚持原创技术分享,您的支持将鼓励我继续创作!