下邊章節中將詳細分析源碼,源碼分析中對於一些邊界的判斷、類型判斷等不作重點分析,主要將分析的重點放在主流程方向上。javascript
createStore 做爲 Redux 的核心 api 之一,其做用是經過 reducer 和 中間件 middleware 構造一個爲 store 的數據結構。關於 createStore 的使用可參考上一章節 Redux源碼分析(1) - Redux介紹及使用 和 官方文檔 createStore 。html
createStore 的源碼結構圖以下圖所示。根據是否傳入enhancer,分別作不一樣的邏輯判斷java
當存在enhancer的時候,實際執行語句以下:es6
//createStore.js
return enhancer(createStore)(reducer, preloadedState); //記爲語句(1)
複製代碼
在上一章節中,咱們介紹過 Redux 的如何使用:redux
//demo
let store = createStore(rootReducer, applyMiddleware(Logger, Test)); //記爲語句(2)
複製代碼
由語句(2)可知:enhancer 其實就是 applyMiddleware 做用中間件(此處爲Logger, Test)的結果。查看 applyMiddleware 源碼,大體結構以下:api
//applyMiddleware.js
function applyMiddleware(...middlewares){
//createStore中對於的enhancer
return createStore => (...args) => {
//...
return {
...store,
dispatch // 覆蓋store中的dispatch
};
}
}
複製代碼
由 applyMiddleware 的源碼可知。語句(1)執行的就是 applyMiddleware 返回高階函數的完整執行,最終返回的結果是包含 store 全部屬性 和 dispatch屬性的一個對象,這也與沒有enhancer時,createStore輸出的結果保持之一。由於 store 對象中自己就存在 dispatch 屬性,由此可知 :bash
applyMiddleware 做用中間件的結果就是更改 store 對象的dispatch。這是前文提到過的,applyMiddleware 本質是對 dispatch 的加強。至因而如何加強的,在下文會詳細分析。數據結構
首先看下此時對應的返回結果app
//createStore.js
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
};
複製代碼
let currentReducer = reducer; // 臨時reducer
let currentState = preloadedState; //當前的state值,默認爲初始化preloadedState
let currentListeners = []; // 監聽隊列,用於存放監聽事件, 發佈-訂閱模式
let nextListeners = currentListeners; // 淺拷貝下這個隊列
let isDispatching = false; // 標誌位,用來判斷是否有存在正在執行的dispatch
複製代碼
各個變量的做用,註釋中已經詳細註明,再也不贅述。dom
根據 Redux api 可知:getState 方法返回當前的 state 樹。currentState 默認值 爲 preloadedState,具體的 currentState 取決於 dispatch(action) 時 reducer 執行後返回的結果。其中若是 isDispatching 爲 true 時,表示有 dispatch 正在執行,此時獲取 state 的值會致使獲取不到正確的 state。
function getState() {
if (isDispatching) {
.....
}
return currentState;
}
複製代碼
先來看看subscriber的使用,其中 listener 表示監聽觸發時,須要作的一些操做。
let unsubscribe = store.subscribe(listener)
unsubscribe()
複製代碼
subscribe 的源碼以下:
function subscribe(listener) {
// 類型判斷
if (typeof listener !== 'function') {
throw new Error('Expected the listener to be a function.');
}
// 同理不能夠dispatch中
if (isDispatching) {
//……
}
// 用來表示訂閱標記,用於避免取消訂閱後再次取消
let isSubscribed = true;
// ensureCanMutateNextListeners幹啥的,點擊去看一下
ensureCanMutateNextListeners();
// 將 listener 存在在 發佈-訂閱模式的監聽隊列 nextListeners 中
nextListeners.push(listener);
// 返回取消的function(unsubscribe)
return function unsubscribe() {
// 若是已經取消訂閱 直接直接return
if (!isSubscribed) {
return;
}
// 同理
if (isDispatching) {
//……
}
// 這裏標記爲 已經取消訂閱
isSubscribed = false;
// 保存訂閱快照
ensureCanMutateNextListeners();
// 根據索引 在監聽隊列裏刪除監聽
const index = nextListeners.indexOf(listener);
nextListeners.splice(index, 1);
};
}
複製代碼
經過源碼的解讀可知。listener 必須傳入一個 function 類型,不然就會報錯。這裏用到了發佈-訂閱模式,執行 subscribe 方法時,將傳入的 listener 存放在 監聽隊列 nextListeners 裏,currentListeners 和 nextListeners 都是引用類型,都是指向的一個內存地址,能夠理解爲是一個東西。
返回值是一個 unsubscribe 函數。執行該函數,就可以取消訂閱。具體來看首先判斷 isSubscribed 是否爲 false,若是是則表明已經取消了該訂閱,再次執行改訂閱則直接忽視。若是 isSubscribed 爲 ture 則表示該訂閱還存在,則經過 indexOf 方法找到索引後,經過 splice 方法,將該訂閱從訂閱隊列中取消,同時不要忘記將 isSubscribed 設置爲已經 false (表示已經取消)。
dispatch的使用方式以下。分發 action是觸發 state 變化的唯一途徑。匹配到對應的 reducer 執行以後,會返回一個新的 state。
store.dispatch(action)
複製代碼
dispatch 的源碼以下:
function dispatch(action) {
// acticon必須是由Object構造的函數, 不然throw Error
if (!isPlainObject(action)) {
// 拋出錯誤
}
// 判斷action, 不存在type throw Error
if (typeof action.type === 'undefined') {
// 拋出錯誤 'Have you misspelled a constant?'
);
}
// dispatch中不能夠有進行的dispatch
if (isDispatching) {
// 拋出錯誤
}
try {
// 執行時標記爲true
isDispatching = true;
// reducer形式以下:(state,action)=>{} , reducer自己就是個函數,因而可知dispatch(action), 就是執行reducer方法,並將currentState,action做爲參數
currentState = currentReducer(currentState, action);
} finally {
// 最終執行, isDispatching標記爲false, 即完成狀態·
isDispatching = false;
}
// 循環監聽隊列,並執行每個監聽事件
const listeners = (currentListeners = nextListeners);
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i];
// 執行每個監聽函數
listener();
}
// 返回傳入的action
return action;
}
複製代碼
經過源碼的解讀可知。action 必須是一個純粹的對象且必須包含type屬性,不然就出拋出異常。dispatch 方法幹了兩件事情:
currentReducer(currentState, action);
若是 reducer 不是組合生成,以 reducer/todos 爲例。則 currentReducer 就是以下:
const todos = (state = [], action) => {
switch (action.type) {
case 'ADD_TODO': //……
case 'TOGGLE_TODO': //……
default:
return state
}
}
若是 reducer 組合生成,也即本例中 rootReducer (經過 combineReducers 組合)。查看 combineReducers 源碼可知, combineReducers 組合各個 reducer 後返回的是一個名爲 combination 的高階函數, 也就是currentReducer ,有以下形式:
function combination(state = {}, action) {
// ……
return state
}
兩者具備相同的形式
(state , action ) =>{ // 更改 state的邏輯}
複製代碼
經過上邊的僞代碼分析能夠,不論是否經過 combineReducers 組合生成, currentReducer 都具備相同的形式, 自己做爲一個函數接受參數 state 和 action ,並根據action,改變state的值。所以能夠認爲,dispatch(action) 時,本質就是 reducer 方法的執行,並將當前的 stare 值 currentState 和 action 做爲參數,並返回一個新的 state。
在 createStore 方法中,還要其餘一些咱們不經常使用的 api
這是一個高級 API。只有在你須要實現代碼分隔,並且須要當即加載一些 reducer 的時候纔可能會用到它。在實現 Redux 熱加載機制的時候也可能會用到observable:
dispatch({ type: ActionTypes.INIT });
const ActionTypes = {
INIT: `@@redux/INIT${randomString()}`,
//……
}
複製代碼
由上邊的代碼可知,初始化 reducer 時是經過 dispatch 一個隨機產生的 action 。根據 reducer 的特性可知,當前初始化的 dispatch 會執行對應的 default 分支,也即會輸出 reducer 中默認的 state 的值。
與 createStore 參數 preloadedState 的對比:在 createStore 方法的定義中能夠接受一個preloadedState 參數,該參數會默認爲當前的 state。
let currentState = preloadedState; //當前的state值,默認爲初始化preloadedState
複製代碼
經過 dispatch 的流程可知,初始化dispatch時,preloadedState 會做爲 reducer 方法執行的參數傳入。當 preloadedState 不存在時,此時 reducer 的入參爲 undefined。一般作法以下, 經過 es6 的默認參數給state 複製初始值,則能起到 preloadedState 的效果。猜想 Redux 這些寫應該是爲了兼容 es5 的默認值處理吧。
const todos = (state = [], action) => {//.......}
複製代碼
可是無論 preloadedState 指定與否,初始化dispatch 執行後,currentState 的值即爲default 分支對應的值。由此可知,咱們定義的 reducer 都要包含 default 分支,不然初始化後 state 的值就會出現異常。
下一章節將分析applyMiddleware,更新中。。。。。。