Redux源码解析03--compose

compose

composeapplyMiddleware的helper函数,用于将多个middleware组成逐级嵌套,使用next进行控制权移交的形式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 将多个function从左到右互相嵌套起来,只有最右边的函数能接收多个参数,其他的都是单参数
* 例如:compose(f, g, h) 等同于 (...args) => f(g(h(...args)))
* @param funcs
* @return {*}
*/
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)))
}

Redux源码解析02--createStore

createStore(reducer, [preloadedState], [enhancer])

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
/**
* 创建一个Redux store,用于保管 state 树
* 只能使用`dispatch()`方法来改变状态
*
* 通常情况下,一个App对应一个store。
* 为了描述 state 树上不同部分如何响应 actions,你需要将多个 reducer 合并(`combineReducers()`)成一个。
*
* @param reducer 合并后的rootReducer,用combineReducers()合并了所有子reducer
* @param preloadedState 初始state,你可以在Server端渲染时,直接指定(前端渲染可以让reducer负责初始化)
* @param enhancer 使用applyMiddleware()来创建enhancer,从而将对应Middleware的能力给予Redux
* @return {*} Redux的store实例,可以读取state,实施action,或是订阅(subscribe)其变化
*/
export default function createStore(reducer, preloadedState, enhancer) {
// 第二个参数传入enhancer(function),而不是preloadedState(Object)时,两者交换
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}

/**
* 如果存在enhancer,将createStore传给enhancer这个闭包,对其进行绑架,从而实现middleware的行为
* 然后再传入reducer和preloadedState,完成构建
*/
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}

return enhancer(createStore)(reducer, preloadedState)
}

// reducer类型检查
if (typeof reducer !== 'function') {
throw new Error('Expected the reducer to be a function.')
}

// 当前的reducer
let currentReducer = reducer
// 记录当前状态
let currentState = preloadedState
// 记录申请subscribe的目标
let currentListeners = []
let nextListeners = currentListeners
// 标志是否正在执行一个action
let isDispatching = false

/**
* 让nextListeners成为currentListeners的副本,而不是指向同一个Array
*/
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice()
}
}

function getState() {/* ... */}
function dispatch() {/* ... */}
function subscribe() {/* ... */}
function replaceReducer() {/* ... */}
function observable() {/* ... */}

// 初始化完成后,触发保留的INIT action
dispatch({ type: ActionTypes.INIT })

return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
}

实例方法:getState()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 返回currentState,即当前的state树
* @return {*} 当前的state树
*/
function getState() {
// 不要在dispatch action时调用这个方法,通常middleware和reducer都会得到一份dispatch前的state作为参数
if (isDispatching) {
throw new Error(
'You may not call store.getState() while the reducer is executing. ' +
'The reducer has already received the state as an argument. ' +
'Pass it down from the top reducer instead of reading it from the store.'
)
}

return currentState
}

实例方法:dispatch(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
34
35
36
37
38
39
40
41
42
43
44
/**
* 执行action,只能通过dispatch()来改变state
* @param action
* @return {*}
*/
function dispatch(action) {
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.')
}

/**
* 给reducer传入currentState和action,从而执行。
* 忽略执行时可能产生的错误
*/
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}

/**
* 逐个调用listener
*/
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}

return action
}

实例方法:subscribe(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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/**
* 将一个监听器添加到currentListeners中。
* 当action被dispatch,并且state树上某些state改变了,它就会被调用。
*
* 如果在listener中调用dispatch()方法,就需要注意:
* 1. 如果在listeners被调用的时候去unsubscribe它,当前一次的执行并不会停止。
* 2. listener不会监测到所有state的变化,因为在它被调用前,dispatch可能已经执行多次。
*
* @param listener {Function} 监听器函数
* @return {unsubscribe} unsubscribe()方法,解除订阅
*/
function subscribe(listener) {
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-reference/store#subscribe(listener) for more details.'
)
}

// 记录当前listener是否有效
let isSubscribed = true

// 制造副本,添加该listener
ensureCanMutateNextListeners()
nextListeners.push(listener)

// 返回unsubscribe()方法
return function unsubscribe() {
// 防止重复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-reference/store#subscribe(listener) for more details.'
)
}

isSubscribed = false

// 制造副本,移除该listener
ensureCanMutateNextListeners()
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
}
}

实例方法:replaceReducer(nextReducer)

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 热切换reducer
* 触发保留的ActionTypes.REPLACE action去发出通知
* @param nextReducer
*/
function replaceReducer(nextReducer) {
if (typeof nextReducer !== 'function') {
throw new Error('Expected the nextReducer to be a function.')
}

currentReducer = nextReducer
dispatch({ type: ActionTypes.REPLACE })
}

实例方法:observable()

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
/**
* ECMAScript中observable形式的实现
*/
function observable() {
const outerSubscribe = subscribe
return {
/**
* The minimal observable subscription method.
* @param {Object} observer Any object that can be used as an observer.
* The observer object should have a `next` method.
* @returns {subscription} An object with an `unsubscribe` method that can
* be used to unsubscribe the observable from the store, and prevent further
* emission of values from the observable.
*/
subscribe(observer) {
if (typeof observer !== 'object' || observer === null) {
throw new TypeError('Expected the observer to be an object.')
}

function observeState() {
if (observer.next) {
observer.next(getState())
}
}

observeState()
const unsubscribe = outerSubscribe(observeState)
return { unsubscribe }
},

[$$observable]() {
return this
}
}
}

Redux源码解析01--utils

actionTypes.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 这些是Redux内部预留的私有action
* 对于任何未知的action,必须返回当前的state树。
* 如果当前的state树还没有初始化,那就返回初始state(每个reducer要定义好它负责的部分state的初始值)。
* 不要在代码中直接访问这些action types。
*/

const randomString = () =>
Math.random()
.toString(36)
.substring(7)
.split('')
.join('.')

const ActionTypes = {
INIT: `@@redux/INIT${randomString()}`,
REPLACE: `@@redux/REPLACE${randomString()}`,
PROBE_UNKNOWN_ACTION: () => `@@redux/PROBE_UNKNOWN_ACTION${randomString()}`
}

export default ActionTypes

isPlainObject.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 检查某个对象是否为JS普通对象
* @param {any} obj 要检查的对象
* @returns {boolean} 如果是普通对象,返回true
*/
export default function isPlainObject(obj) {
if (typeof obj !== 'object' || obj === null) return false

// 递归原型链,得到除null外,最内层的__proto__(对于引用类型而言,通常是Object.prototype)
let proto = obj
while (Object.getPrototypeOf(proto) !== null) {
proto = Object.getPrototypeOf(proto)
}

return Object.getPrototypeOf(obj) === proto
}