createStore
createStore
只包含一个createStore
函数,用来创建store
;该函数输入reducer
函数、初始状态preloadedState
以及对 store 功能进行增强的高阶函数enhancer
。
Redux 提供的数据流管理的功能基本就集成在这个函数中了。
入参处理
if (
(typeof preloadedState === 'function' && typeof enhancer === 'function') ||
(typeof enhancer === 'function' && typeof arguments[3] === 'function')
) {
...
}
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
...
}
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
...
}
return enhancer(createStore)(reducer, preloadedState)
}
if (typeof reducer !== 'function') {
...
}
函数开始部分的这一大堆判断代码都是用来处理参数的,允许用户以以下方式调用createStore
:
createStore(reducer, preloadedState, enhancer);
createStore(reducer, preloadedState);
createStore(reducer, enhancer);
createStore(reducer);
如果出现了不符合上述情况的入参,则会报错。
变量及函数定义
接下来定义了一些函数内部变量:
// 当前的 reducer 函数
let currentReducer = reducer
// 当前状态
let currentState = preloadedState
// 当前的事件处理函数
let currentListeners = []
// 下个阶段的事件处理函数
let nextListeners = currentListeners
// 指示是否在执行 reducer 函数
let isDispatching = false
然后定义了一些函数来操作上述的变量,实现 store 的功能,并最后将它们作为一个模块导出。
getState
getState
用来获取当前的全局状态,逻辑较为简单:
- 当 reducer 执行时不允许通过这个函数获取状态,因为 reducer 应当是一个纯函数,它可以从参数中拿到最新的状态,而不应该从函数外部的某个地方去获取,详情可以看 StackOverflow 上的这个讨论:Calling store.getState() in a reducer function, is that an anti pattern?;
- 如果没有在执行 reducer 函数,则返回
currentState
,即当前状态。
/**
* Reads the state tree managed by the store.
*
* @returns {any} The current state tree of your application.
*/
function getState() {
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
}
subscribe 和 ensureCanMutateNextListeners
subscribe
函数接收一个函数作为listener
,将它加入订阅者数组中,并返回一个取消订阅的函数。
在批量调用订阅者的时候,即使又调用subscribe
进行了订阅或取消订阅,也不会改变当前执行的任务队列,而是会将改变保存在nextListener
中,否则可能造成有些订阅者没有被调用的情况;每次批量调用之前都会进行快照才会取得最新的订阅者数组。
function subscribe(listener) {
// 订阅者必须是函数
if (typeof listener !== 'function') {
throw new Error(
`Expected the listener to be a function. Instead, received: '${kindOf(
listener
)}'`
)
}
// reducer 执行过程中不允许改变订阅者,因为 reducer 必须是纯函数,不能有任何副作用
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
// 确保 nextListeners 和 currentListeners 不是一个数组
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
}
}
值得一提的是,当要改变nextListener
之前,要调用一次ensureCanMutateNextListeners
函数,确保nextListeners
和currentListeners
指向的不是同一个数组,这样才能只改变nextListeners
而不改变currentListeners
。
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
// 如果两个变量指向同一个数组,则复制一份新的数组作为 nextListeners
nextListeners = currentListeners.slice()
}
}
dispatch
dispatch
用来分发一个action
,并将它传入reducer
函数,来对全局状态树进行修改;并执行所有的回调函数。
dispatch
的入参为一个action
,这个action
必须是一个 plainObject,还必须有一个type
字段,函数会对入参进行检查,如果传入了不满足条件的aciton
,就会抛出错误。
isDispatching
在调用reducer
的过程中会被设置为true
。在调用reducer
之前,会判断当前是否正在调用reducer
,如果是则抛出一个错误,因为reducer
应当是一个纯函数,只用来改变state
,如果在reducer
中再进行dispatch
,当第一个reducer
执行完之后,结果会覆盖第二个reducer
调用的结果,可能会导致状态改变的丢失。
调用完成之后,将reducer
的返回值设置为新的state
,然后对当前的nextListeners
进行快照,确定此次需要调用的任务队列,并调用所有的订阅者。保存快照是因为在调用listeners
的时候,有可能会对listeners
这个数组进行修改,而此次通知的应当是执行完reducer
那一时刻的所有订阅者,因此需要先快照保存下来进行调用。
最后,将action
返回,便于下一个中间件进行调用。
function dispatch(action) {
if (!isPlainObject(action)) {
throw new Error(
`Actions must be plain objects. Instead, the actual type was: '${kindOf(
action
)}'. You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions. See https://redux.js.org/tutorials/fundamentals/part-4-store#middleware and https://redux.js.org/tutorials/fundamentals/part-6-async-logic#using-the-redux-thunk-middleware for examples.`
)
}
if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. You may have misspelled an action type string constant.'
)
}
// 不能在 reducer 中调用 dispatch
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
}
try {
// 置为 true,表明正在调用 reducer
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
// 将 nextListeners 赋值给 currentListeners ,并快照
const listeners = (currentListeners = nextListeners)
// 调用所有 listeners
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
return action
}
replaceReducer
用于更改 reducer 函数。如果某些模块是动态加载的,当动态加载过来了以后,reducer函数可能需要发生变化,这里就提供了一个方法来修改当前的reducer函数。模块热加载的机制下也需要使用它。
然后 dispatch 一个 类型为REPLACE
的 action,来执行一次 reducer ,创建新的全局状态树。
function replaceReducer(nextReducer) {
if (typeof nextReducer !== 'function') {
throw new Error(
`Expected the nextReducer to be a function. Instead, received: '${kindOf(
nextReducer
)}`
)
}
currentReducer = nextReducer
// This action has a similiar effect to ActionTypes.INIT.
// Any reducers that existed in both the new and old rootReducer
// will receive the previous state. This effectively populates
// the new state tree with any relevant data from the old one.
dispatch({ type: ActionTypes.REPLACE })
}
observable
这个函数用于将创建的store
配置成可观察对象。
store
作为一个可观察对象,应当有一个subscribe
方法,可以传入一个观察者对象,加入这个可观察对象的观察者列表中;当可观察对象发生变化时,会调用所有订阅了它的观察者的next
方法,并将当前的状态传入进去。
Redux 通过在store
对象上部署Symbol.observable
这个属性来实现可观察对象。该属性是一个函数,返回一个对象,有subscribe
方法和Symbol.observable
方法,subscribe
方法通过订阅回调函数的方式,实现了在state
发生变化时,调用观察者的next
方法,使store
也成为了一个可观察对象,可以通过订阅观察者的方式来使用。
function observable() {
const outerSubscribe = subscribe
return {
subscribe(observer) {
// 观察者对象必须是一个 object
if (typeof observer !== 'object' || observer === null) {
throw new TypeError(
`Expected the observer to be an object. Instead, received: '${kindOf(
observer
)}'`
)
}
// 当和状态发生改变的时候,
function observeState() {
if (observer.next) {
observer.next(getState())
}
}
// 初次订阅,执行一次观察者的 next
observeState()
// 将 observeState 作为订阅者,这样每次状态变化时,都会调用观察者的 next 方法
const unsubscribe = outerSubscribe(observeState)
return { unsubscribe }
},
[$$observable]() {
return this
},
}
}
初始化
dispatch 一个 action,触发 reducer 的初始化执行,用来构建状态树的初始化状态。
dispatch({ type: ActionTypes.INIT })
返回 store 对象
最后,返回store
对象,它是一个包含了dispatch
/subscribe
/getState
/replaceReducer
/$$observable
属性的对象,外界通过操作对象的这些方法来对store
进行操作。
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable,
}