Redux原始碼分析(createStore)
使用redux都快3年了,到現在也沒認真去了解一下原始碼罪過啊,所以需要對它進行一些分析和學習,一方面能更好的去使用它,另一方面也學習一下該框架的設計思路,首先我們看到 redux/src/index.js 檔案
export {
createStore,
combineReducers,
bindActionCreators,
applyMiddleware,
compose,
__DO_NOT_USE__ActionTypes
}
複製程式碼
所以主要部分也就是上面幾個函式,我們下面一點點去分析每一個功能點
createStore
其實該檔案有著大量的註釋了,大家可以先簡單的看看程式碼中的註釋瞭解一遍。 其實createStore就是返回了一個物件,這個物件只有幾個方法而已,而我們常用的就是dispatch,subscribe,getState這三個了
* Creates a Redux store that holds the state tree.
* The only way to change the data in the store is to call `dispatch()` on it.
複製程式碼
該函式的作用呢就是建立一個store(廢話嘛),那麼什麼是store呢?我的理解 就是一個倉庫,存著整個程式的狀態,且只能有一個,就是用這一個store搞定專案中的全部狀態,當然不論多大的專案,統統只有這一個,弄兩個肯定不好使,並且只有一個路子去修改裡面的資料,那麼就是呼叫dispatch()
function createStore(reducer, preloadedState, enhancer)
複製程式碼
可以看到函式體主要三個引數,簡單說下
- reducer:它是一個函式可以表達為:
(preState,action) => newState
就是說根據action和之前的狀態,返回一個新的狀態(這裡的新是新構建,而不是修改過,這點切記) - preloadedState:字面理解即可,預先載入的狀態,即初始狀態
- enhancer:這個需要拿出篇幅來說了,增強劑,增強createStore
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
複製程式碼
也容易理解,在只有兩個引數的情況,並且第二個為funcion的時候,那麼第二個引數就是enhancer了,互動一下引數位置
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
return enhancer(createStore)(reducer, preloadedState)
}
複製程式碼
對enchancer型別判斷,可以看到enhancer就是將store傳入,在內部處理之後再將store返回來,繼續傳入reducer和初始狀態進行構建
let currentReducer = reducer
let currentState = preloadedState
let currentListeners = []
let nextListeners = currentListeners
let isDispatching = false
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice()
}
}
複製程式碼
建立一些變數儲存reducer,state,以及訂閱器儲存在nextListeners中,ensureCanMutateNextListeners相當於每次對當前的訂閱器進行備份,因為每次訂閱一個listener的時候都是對nextListeners陣列進行新增
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)
}
}
複製程式碼
訂閱器,註冊監聽函式,每一個listener都是一個func,並且返回了一個取消註冊監聽的函式unScribe,用於刪除listener,其實就是將一個個的函式新增到陣列中,之後每次在store發生變化的時候(其實也就是呼叫dispatch的時候)就會觸發它~
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.')
}
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
}
複製程式碼
上面呢就是這裡比較重要的dispatch函式了,其實非常簡單,主要就是它:
currentState = currentReducer(currentState, action)
複製程式碼
將當前的全部狀態和action傳入reducer,得到新的state,這樣就完成了state的狀態更新了,之後再去遍歷全部的listeners,從而在各個listeners的內部去更新view,完成整個流程(返回值其實還是這個action物件),其實以上內容和三個函式應該是redux的核心內容了,下面用一種簡單的總結來表達一下store(借鑑的阮一峰的)
const createStore = (reducer) => {
let state;
let listeners = [];
const getState = () => state;
const dispatch = (action) => {
state = reducer(state, action);
listeners.forEach(listener => listener());
};
const subscribe = (listener) => {
listeners.push(listener);
return () => {
listeners = listeners.filter(l => l !== listener);
}
};
dispatch({});
return { getState, dispatch, subscribe };
};
複製程式碼
而該檔案還有兩個函式分別是
function replaceReducer(nextReducer)
function observable()
複製程式碼
第一個很明顯替換整個reducer用,在一些熱載入場景應該會用到 而第二個暫時還不太理解作者意圖,用觀察者替換訂閱釋出嗎?暫時先不去想 以上就是整個createStore的原始碼分析~
更多內容與討論可以參考我的github.com/jinjiaxing/…