以前,已經寫過一篇redux源碼解讀(一),主要分析了 redux
的核心思想,並用100多行代碼實現一個簡單的 redux
。可是,那個實現還不具有合併 reducer
和添加 middleware
的功能。git
今天咱們一塊兒來看看合併 reducer
(即 combineReducers
) 的原理和實現。github
在分析原理以前,先來看看combineReducers
的用法:redux
import { createStore, combineReducers } from 'redux'; const addScore = (state, action) => {}; const deleteScore = (state, action) => {}; const rootReducer = combineReducers({addStore, deleteScore}); const store = createStore(rootReducer);
從上面的例子能夠看出,combineReducers
接收的參數類型是一個原生對象,其中這個對象的每一個鍵值都是一個 reducer
純函數。另外,由於 combineReducer()
返回的結果能夠傳遞給createStore
做爲參數,能夠推出它返回的結果也是一個 reducer
函數。函數
瞭解了 combineReducer
用法以後 ,那開始一步一步的用代碼來實現其功能吧。prototype
首先,須要先聲明 combineReducers
的參數,而後判斷傳進來的參數是否是原生對象類型( plain object
),若是不是,則拋出異常,若是是,則須要獲取該對象的全部屬性(key)並存放到變量 reducerKeys
裏面,而後對這些key進行遍歷,過濾掉那些不是函數的值,並將結果放到 finalReducerKeys
裏面。代碼以下:code
export default function combineReducers(reducers) { // 判斷參數reducers是否爲對象 if(Object.prototype.toString.call(reducer) !== '[object Object]') { throw new Error('combineReducers expected plain object params'); } const reducerKeys = Object.keys(reducers); let finalReducerKeys = []; // 過濾掉value不是Function類型的鍵名,而後將結果放到fianlReducerKeys裏面 reducerKeys.forEach((key, i) => { if(typeof reducers[key] === 'function') { finalReducerKeys.push(key); } }); }
前面已經提到過了 combineReducers
返回的結果是一個純函數。那這個返回的函數須要處理些什麼邏輯呢?由於他合併了其餘的 reducers
,因此須要遍歷這些 reducer
並執行他們。而後,並對比一下執行 reducer
以後的數據有沒有變化 ,若是有變化則返回新的 state
, 不然直接返回以前的 state
。代碼以下:對象
export default function combineReducers(reducers) { // 省略和前面相同的部分 // 返回一個新的、通過組合的reducer函數 return function(state = {}, action) { let hasChanged = false; const nextState = {}; // 遍歷finalReducerKeys,並調用對應的reducer。 finalReducerKeys.forEach((key, i) => { const stateForKey = state[key]; const nextStateForKey = reducers[key](stateForKey, action); nextState[key] = nextStateForKey; // 若是先後狀態不同,則hasChanged設爲true if(stateForKey !== nextStateForKey) { hasChanged = true; } }); // 若是有變化,則返回新的state,不然返回舊的 return hasChanged ? nextState : state; } }
OK,《redux
源碼解讀(二)》就寫到這裏,今天週五啦,祝你們週末愉快哈!若是對 combineReducer
還有不明白的地方,歡迎留言討論哈。另外,可能有些地方我分析得不到位的,建議到個人github去下載代碼本身再好好研究一下。重要的事情說三遍:代碼在這裏下載! 代碼在這裏下載! 代碼在這裏下載!blog