createStore
一般而言,我檢視一個庫的原始碼,首先回檢視對應方法的引數,其次是對應的return ,然後再看程式碼的具體實現。
通過檢視原始碼,發現createStore 方法返回了一個物件, 該物件共暴露出了五個方法,四個常用的方法:
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
複製程式碼
檢視原始碼的開始部分,我們發現createStore可以傳入兩個三個引數:
export default function createStore(reducer, preloadedState, enhancer)
複製程式碼
其中第一個引數reducer 是必須要傳遞的而且必須是一個函式,不然Redux回報一場
if (typeof reducer !== 'function') {
throw new Error('Expected the reducer to be a function.')
}
複製程式碼
如果傳遞了第二個引數preloadedState,而且第二個引數不是一個function , 則將preloadedState 儲存在內部變數currentState中, 也就是我們給State 的預設狀態
如果preloadedState 是一個function , 則將preloadedState 賦值給enhancer
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
// 執行enhancer, 一般enhancer 就是一組中介軟體
return enhancer(createStore)(reducer, preloadedState)
}
if (typeof reducer !== 'function') {
throw new Error('Expected the reducer to be a function.')
}
複製程式碼
該方法中儲存了三個重要的變數:
let currentReducer = reducer
let currentState = preloadedState
let currentListeners = []
複製程式碼
- currentReducer 儲存了所有的Reducer
- currentState 將狀態資料都儲存在這裡,也是Redux 運算元據的唯一物件
- currentListeners 會儲存對Redux State 訂閱的監聽者
我們已經知道了Redux能控制的如上三個主要內部變數了, 接下拉我們會 根據createStore 暴露出來的五個方法,來學習怎麼去操作這三個變數
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
複製程式碼
dispatch(去掉驗證程式碼)
function dispatch(action) {
try {
isDispatching = true
// 呼叫Reduce 對action 進行處理,每次傳入原始的state.返回更新後的states
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
}
複製程式碼
首先需要傳遞一個action 引數來告訴需要做的操作物件,action 只能是一個Object, 而且必須包含type 欄位
if (!isPlainObject(action)) {
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?'
)
}
複製程式碼
我們會呼叫內部變數currentReducer 去處理髮起的對應的action
currentState = currentReducer(currentState, action)
複製程式碼
我們會發現currentReducer 其實是一個function, 而且需要兩個引數: currentState , action.
currentReducer 返回的值賦值給currentState, 由createStore 傳入引數的分析得知,preloadedState 只能是要給Object, 所以currentReducer function 返回的是要給Object.
從這一行程式碼我們可以總結得到:
reducer 其實就是一個函式,根據action 對 currentState 進行處理,並且返回新的currentState 的函式
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
複製程式碼
由上面一段程式碼我們可以得知,在更新state , 需要去遍歷執行所有的監聽者(listener),讓監聽者得知state變更的資訊
subscribe(訂閱)
subscribe顧名思義就是訊息訂閱的意思
function subscribe(listener) {
let isSubscribed = true
ensureCanMutateNextListeners()
nextListeners.push(listener)
return function unsubscribe() {
if (!isSubscribed) {
return
}
isSubscribed = false
ensureCanMutateNextListeners()
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
}
}
複製程式碼
很簡單的一段程式碼, 傳入的引數listener 就是一個訂閱者方法,
nextListeners.push(listener)
複製程式碼
將listener 儲存在內部變數陣列中。返回一個unsubscribe方法, 用來取消訂閱
nextListeners.splice(index, 1) // 只要從陣列中刪除listener 就是取消訂閱了
複製程式碼
getState(獲取狀態)
function getState() {
return currentState
}
複製程式碼
非常簡單,就是返回內部變數currentState
replaceReducer(替換reducer)
function replaceReducer(nextReducer) {
currentReducer = nextReducer
dispatch({ type: ActionTypes.REPLACE })
}
複製程式碼
就是替換當前的reducer.
總結
- 首先我們需要呼叫createStore 方法建立一個store
let store = createStore(reducer, preloadedState)
複製程式碼
reducer 是一個function, 而且必須要 傳遞兩個引數,第一個是state, 第二個是一個action
- 利用相關事件觸發store.dispatch(action)去變更狀態
這個action 必須要有type 屬性
- 訂閱狀態變更store.subscribe(listener)
- 在listener中去獲取最新state(store.getState()),然後做去相應的處理