Redux原始碼分析(一)
使用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/…