Source Time
import $$observable from 'symbol-observable'
import ActionTypes from './utils/actionTypes'
import isPlainObject from './utils/isPlainObject'
export default function createStore(reducer, preloadedState, enhancer) {
// 第一個參數為reducer达皿,函數類型
// 第二個參數應為初始state姐刁,類型任意
// 第三個參數為enhancer芥牌,函數類型,即applyMiddleware返回的函數
if (
(typeof preloadedState === 'function' && typeof enhancer === 'function') ||
(typeof enhancer === 'function' && typeof arguments[3] === 'function')
) {
// 若preloadedState和enhancer均為函數聂使,
// 或者參數個數多余三個且最后兩個都是函數類型壁拉,
// 猜測使用了多個enhancer,拋出錯誤
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') {
// 若preloadedState為函數類型柏靶,且enhancer為空弃理,
// 猜測無初始state,第二個參數直接為enhancer
enhancer = preloadedState
preloadedState = undefined
}
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
// 若enhancer存在但不是函數類型屎蜓,則拋出錯誤痘昌,提示enhancer應該是一個函數
throw new Error('Expected the enhancer to be a function.')
}
// 若enhancer存在并且是函數類型,調用enhancer對遞歸入參createStore
// 這里可以結合applyMiddleware理解,enhancer即為applyMiddleware的返回結果
return enhancer(createStore)(reducer, preloadedState)
}
if (typeof reducer !== 'function') {
// 判斷reducer類型辆苔,非函數類型就報錯
throw new Error('Expected the reducer to be a function.')
}
// 當前reducer
let currentReducer = reducer
// 當前state
let currentState = preloadedState
// 當前事件監(jiān)聽數組
let currentListeners = []
// 下一輪事件監(jiān)聽數組算灸,此處與ensureCanMutateNextListeners相關聯
let nextListeners = currentListeners
// 判斷是否正在dispatch的標志
let isDispatching = false
function ensureCanMutateNextListeners() {/* ... */}
function getState() {/* ... */}
function subscribe(listener) {/* ... */}
function dispatch(action) {/* ... */}
function replaceReducer(nextReducer) {/* */}
function observable() {/* ... */}
// When a store is created, an "INIT" action is dispatched so that every
// reducer returns their initial state. This effectively populates
// the initial state tree.
dispatch({ type: ActionTypes.INIT })
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
}
Analysis
由于createStore
內部比較大,所以這里我將一些內部定義的函數拎出單獨描述作用驻啤,對于其他的部分可參考中文注釋內容菲驴。最后的英文注釋也很好的描述了在創(chuàng)建store之后通過dispatch一次名字叫作INIT
的action來進行整個store的內部state初始化〗钟樱總結一下非函數部分內部的功能就是以下幾點內容:
- 判斷入參是否符合要求谢翎,即只能最多一個enhancer
- 兼容第二個參數為enhancer而沒有初始state的情況
- 處理有enhancer的創(chuàng)建邏輯
- 判斷reducer的正確性
- 定義內部功能變量
- 定義內部功能函數或提供給用戶的API
- 初始化store,初始化state
- 導出API
講完了非函數部分內容沐旨,接下來一個一個分析一下在createStore
中定義的函數森逮。
ensureCanMutateNextListeners
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice()
}
}
由于nextListeners
變量定義的時候是根據currentListeners
獲得的數組引用(參考定義部分),所以為了不影響當前事件監(jiān)聽數組磁携,函數ensureCanMutateNextListeners
會在需要更改nextListeners
的時候先判斷一下是否是當前事件監(jiān)聽數組的引用褒侧,若是,則會用slice
方法獲得一個同樣元素的不同數組作為新的nextListeners
谊迄。
getState
function getState() {
// 獲取當前store中state闷供,如果正在dispatch則報錯
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
}
函數getState
主要用于返回當前store中的state對象,需要注意的是如果當前store正在進行dispatch
操作统诺,那么就不能獲取state歪脏,而是拋出一個錯誤提示。
subscribe
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.'
)
}
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-reference/store#subscribe(listener) for more details.'
)
}
isSubscribed = false
ensureCanMutateNextListeners()
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
}
}
這里的subscribe
函數主要用于添加用戶事件的監(jiān)聽粮呢,會在dispatch
更新state后進行調用(詳情在dispatch
部分說明)婿失,即將監(jiān)聽事件加入到nextListeners
。需要注意的是啄寡,該函數返回的是一個用于解除監(jiān)聽的unsubscribe
方法豪硅,這里利用的是閉包的經典用法,可以參考學習一下挺物。
dispatch
function dispatch(action) {
if (!isPlainObject(action)) {
// 判斷是否是符合要求的plain object
throw new Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.'
)
}
if (typeof action.type === 'undefined') {
// 判斷是否包含所需的type屬性
throw new Error(
'Actions may not have an undefined "type" property. ' +
'Have you misspelled a constant?'
)
}
if (isDispatching) {
// 利用isDispatching判斷當前是否正在進行dipatch操作
throw new Error('Reducers may not dispatch actions.')
}
// 更新標示懒浮,并利用當前reducer更新state
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
// 通過nextListeners獲得最新的當前事件監(jiān)聽數組
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
// 遍歷觸發(fā)監(jiān)聽事件
const listener = listeners[i]
listener()
}
// 返回入參action
return action
}
dispatch
函數可以說是用戶接觸最多的一個api了,功能非常強大识藤,但是它的實現卻是非常好理解的砚著。
- 首先是對接受的入參——
action
進行類型校驗,判斷是否是合法的plain object痴昧; - 其次是判斷這個
action
是否包含標示更新類型的type
屬性赖草; - 利用isDispatching判斷是否正在進行
dispatch
操作,如果是則拋出錯誤剪个; - 更新isDispatching標志,并利用將
action
作為入參傳入currentReducer
得到最新的當前state; - 通過
nextListeners
獲取最新的事件監(jiān)聽數組扣囊,同時遍歷觸發(fā)每個監(jiān)聽事件乎折; - 返回入參
action
備用。
replaceReducer
function replaceReducer(nextReducer) {
if (typeof nextReducer !== 'function') {
// 判斷nextReducer是否是符合要求的函數類型
throw new Error('Expected the nextReducer to be a function.')
}
// 更新當前reducer為最新的reducer
currentReducer = nextReducer
// 觸發(fā)一次REPLACE類型的action用于使用最新的reducer更新當前store中state數據
dispatch({ type: ActionTypes.REPLACE })
}
replaceReducer
函數接受一個新的reducer函數——nextReducer
作為入參侵歇,在內部替換掉currentReducer
骂澄,同時主動dispatch一次REPLACE
類型的私有action,用于應用最新的reducer方法更新state惕虑。從我的項目經歷來說坟冲,這個replaceReducer
方法以及接下來的observable
方法,使用頻率都不是太高溃蔫,不知道具體使用場景會是什么樣子以及其他人的情況如何健提。
observable
function observable() {
// 根據subscribe方法定義outerSubscribe方法,備用
const outerSubscribe = subscribe
// 返回一個包含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) {
// 接受一個對象作為觀察者observer
if (typeof observer !== 'object' || observer === null) {
// 校驗observer類型
throw new TypeError('Expected the observer to be an object.')
}
// 定義一個監(jiān)聽state的方法
function observeState() {
if (observer.next) {
// 運行observer對象的next方法伟叛,以當前store的state作為入參
observer.next(getState())
}
}
// 執(zhí)行一次observerState方法
observeState()
// 定義解除監(jiān)聽的方法私痹,并作為一個對象的屬性,返回該對象
const unsubscribe = outerSubscribe(observeState)
return { unsubscribe }
},
// 獲取當前對象的this指向
[$$observable]() {
return this
}
}
}
坦白說统刮,之前從來沒有接觸紊遵、使用過這個api,所以對于其作用知之甚少侥蒙,暫時只能通過代碼層面來解讀其作用暗膜,后面可以進一步了解一下。
All
index
compose
applyMiddleware
bindActionCreators
combineReducers
createStore