前言
看完 Redux文档,对Redux的内部运作不是很了解,大致问题记录如下:
- 容器组件怎么通过connect将store里的数据传入展示型组件的props中?
- mapStateToProps与mapDispatchToProps怎么将state与dispatch的值组装到props?
- action是什么?dispatch action做了些什么操作?
- 若dispatch 一个函数,则函数默认参数有dispatch与state,自定义的函数怎么能默认接受这两个参数的?
带着这些问题打开Redux源码](https://github.com/reduxjs/redux
),发现才一年的时间没做前端,Redux源码都从js换成ts了,屁颠屁颠跑去看TS文档,没咋看懂,但是应该能上源码了。
源码结构
首先看看文件夹目录结构
几个常用的api都在src/下,再下一级目录就是封装的一些类型,这些后面再详细描述,先开始上源码!
index.ts
这个文件主要导出了一些常用的接口1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import createStore from './createStore'
import combineReducers from './combineReducers'
import bindActionCreators from './bindActionCreators'
import applyMiddleware from './applyMiddleware'
import compose from './compose'
import warning from './utils/warning'
import __DO_NOT_USE__ActionTypes from './utils/actionTypes'
//中间省略一些代码
···
export {
createStore,
combineReducers,
bindActionCreators,
applyMiddleware,
compose,
__DO_NOT_USE__ActionTypes
}
可以看到我们Redux里常用的api,createstore、combineReducers、applyMiddleware、compose都给导出了,bindActionCreators之前没有用到过,正好可以看一下实现和用法。
按照导出顺序挨个看看这些文件
createStore.ts
这个文件重载了两个createStore方法,主要区别是有无 preloadedState 这个参数。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19export default function createStore<
S,
A extends Action,
Ext = {},
StateExt = never
>(
reducer: Reducer<S, A>,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
export default function createStore<
S,
A extends Action,
Ext = {},
StateExt = never
>(
reducer: Reducer<S, A>,
preloadedState?: PreloadedState<S>,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
用UML画了个假装是createStore的类图,主要是为了方便看这个函数里面定义的一些属性和方法。
函数申明1
2
3
4
5
6
7
8
9
10export default function createStore<
S,
A extends Action,
Ext = {},
StateExt = never
>(
reducer: Reducer<S, A>,
preloadedState?: PreloadedState<S> | StoreEnhancer<Ext, StateExt>,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
这个函数接收两到三个参数,其中,若只传入两个参数,且第二个参数为函数,会把第二个参数preloadedState赋值给enhancer,然后preloadedState赋值为undefined。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
30if (
(typeof preloadedState === 'function' && typeof enhancer === 'function') ||
(typeof enhancer === 'function' && typeof arguments[3] === 'function')
) {
throw new Error(
'It looks like you are passing several store enhancers to ' +
'createStore(). This is not supported. Instead, compose them ' +
'together to a single function.'
)
}
// 这里,其他都是类型检测
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState as StoreEnhancer<Ext, StateExt>
preloadedState = undefined
}
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
return enhancer(createStore)(
reducer,
preloadedState as PreloadedState<S>
) as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
}
if (typeof reducer !== 'function') {
throw new Error('Expected the reducer to be a function.')
}
然后就是方法内其他函数的介绍
dispatch(action)
该方法主要做了如下三个操作:
1、修改 isDispatching的值(这个属性默认值为false,定义这个有点类似于锁的思想,阻止其他操作的修改)。
2、执行listeners[]数组里的每个listener方法。
3、返回action(这个action为函数传入的action)
代码如下: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
34function dispatch(action: A) {
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?'
)
}
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
}
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
return action
}
subscribe(listener)
添加一个变化监听器,每当 dispatch action 的时候就会执行。
该方法主要有如下操作:
1、将listener推入listeners队列,等待dispatch action时调用。
2、修改isSubscribe的值。
3、返回unsubscribe函数。(函数内操作:1、修改isSubscribe值;1、listeners队列删除listener)
代码如下: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
39function subscribe(listener: () => void) {
if (typeof listener !== 'function') {
throw new Error('Expected the listener to be a function.')
}
if (isDispatching) {
throw new Error(
'You may not call store.subscribe() while the reducer is executing. ' +
'If you would like to be notified after the store has been updated, subscribe from a ' +
'component and invoke store.getState() in the callback to access the latest state. ' +
'See https://redux.js.org/api/store#subscribelistener for more details.'
)
}
let isSubscribed = true
ensureCanMutateNextListeners()
nextListeners.push(listener)
return function unsubscribe() {
if (!isSubscribed) {
return
}
if (isDispatching) {
throw new Error(
'You may not unsubscribe from a store listener while the reducer is executing. ' +
'See https://redux.js.org/api/store#subscribelistener for more details.'
)
}
isSubscribed = false
ensureCanMutateNextListeners()
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
currentListeners = null
}
}
replateReducer(nextReducer)
这个方法没怎么用到过,代码写法也有地方没看懂。。。
主要操作:
1、将当前reducer替换为nextReducer。
2、dispatch一个replace action。(dispatch的操作就是上面那个函数里的,修改属性状态啦,执行所有listener啦,返回action啦)
3、返回store。
代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22function replaceReducer<NewState, NewActions extends A>(
nextReducer: Reducer<NewState, NewActions>
): Store<ExtendState<NewState, StateExt>, NewActions, StateExt, Ext> & Ext {
if (typeof nextReducer !== 'function') {
throw new Error('Expected the nextReducer to be a function.')
}
;((currentReducer as unknown) as Reducer<
NewState,
NewActions
>) = nextReducer
dispatch({ type: ActionTypes.REPLACE } as A)
// change the type of the store by casting it to the new store
return (store as unknown) as Store<
ExtendState<NewState, StateExt>,
NewActions,
StateExt,
Ext
> &
Ext
}
createStore文件的主要内容就是这些,还有其他一些类型检测以及工具函数的代码未放上来,这个源码里都有。