Redux做爲大型React應用狀態管理最經常使用的工具。它是一個應用數據流框架,與Flux框架相似。它是零依賴的,能夠配合其餘框架或者類庫一塊兒使用。雖然在平時的工做中不少次的用到了它,可是一直沒有對其原理進行研究。最近看了一下源碼,下面是我本身的一些簡單認識。redux
結合使用場景咱們首先來看一下createStore方法。閉包
// 這是咱們日常使用時建立store const store = createStore(reducers, state, enhance);
如下源碼爲去除異常校驗後的源碼,app
export default function createStore(reducer, preloadedState, enhancer) { // 若是有傳入合法的enhance,則經過enhancer再調用一次createStore if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { throw new Error('Expected the enhancer to be a function.') } return enhancer(createStore)(reducer, preloadedState) // 這裏涉及到中間件,後面介紹applyMiddleware時在具體介紹 } let currentReducer = reducer //把 reducer 賦值給 currentReducer let currentState = preloadedState //把 preloadedState 賦值給 currentState let currentListeners = [] //初始化監聽函數列表 let nextListeners = currentListeners //監聽列表的一個引用 let isDispatching = false //是否正在dispatch function ensureCanMutateNextListeners() {} function getState() {} function subscribe(listener) {} function dispatch(action) {} function replaceReducer(nextReducer) {} // 在 creatorStore 內部沒有看到此方法的調用,就不講了 function observable() {} //初始化 store 裏的 state tree dispatch({ type: ActionTypes.INIT }) return { dispatch, subscribe, getState, replaceReducer, [$$observable]: observable } }
咱們能夠看到creatorStore方法除了返回咱們經常使用的方法外,還作了一次初始化過程dispatch({ type: ActionTypes.INIT });那麼dispatch幹了什麼事情呢?框架
/** * dispath action。這是觸發 state 變化的唯一途徑。 * @param {Object} 一個普通(plain)的對象,對象當中必須有 type 屬性 * @returns {Object} 返回 dispatch 的 action */ function dispatch(action) { // 判斷 dispahch 正在運行,Reducer在處理的時候又要執行 dispatch if (isDispatching) { throw new Error('Reducers may not dispatch actions.') } try { //標記 dispatch 正在運行 isDispatching = true //執行當前 Reducer 函數返回新的 state 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主要作了二件事情異步
那麼reducer是怎麼改變state的呢?這就涉及到下面的combineReducers了。函數
回到上面建立store的參數reducers,工具
// 兩個reducer const todos = (state = INIT.todos, action) => { // .... }; const filterStatus = (state = INIT.filterStatus, action) => { // ... }; const reducers = combineReducers({ todos, filterStatus }); // 這是咱們日常使用時建立store const store = createStore(reducers, state, enhance);
下面咱們來看combineReducers作了什麼源碼分析
// 第一次篩選,參數reducers爲Object // 篩選掉reducers中不是function的鍵值對 const reducerKeys = Object.keys(reducers) const finalReducers = {} for (let i = 0; i < reducerKeys.length; i++) { const key = reducerKeys[i] if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key] } } const finalReducerKeys = Object.keys(finalReducers) // 二次篩選,判斷reducer中傳入的值是否合法(!== undefined) // 獲取篩選完以後的全部key let shapeAssertionError try { assertReducerShape(finalReducers) } catch (e) { shapeAssertionError = e } return function combination(state = {}, action) { let hasChanged = false const nextState = {} // 遍歷全部的key和reducer,分別將reducer對應的key所表明的state,代入到reducer中進行函數調用 for (let i = 0; i < finalReducerKeys.length; i++) { const key = finalReducerKeys[i] const reducer = finalReducers[key] // 這裏就是reducer function的名稱和要和state同名的緣由,傳說中的黑魔法 const previousStateForKey = state[key] const nextStateForKey = reducer(previousStateForKey, action) if (typeof nextStateForKey === 'undefined') { const errorMessage = getUndefinedStateErrorMessage(key, action) throw new Error(errorMessage) } // 將reducer返回的值填入nextState nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey !== previousStateForKey } // 發生改變了返回新的nextState,不然返回原先的state return hasChanged ? nextState : state } }
看到這初始化流程已經走完了。這個過程咱們認識了dispatch和combineReducers;接下來咱們來看一下咱們本身要怎麼更新數據。學習
用戶更新數據時,是經過createStore後暴露出來的dispatch方法來觸發的。dispatch 方法,是 store 對象提供的更改 currentState 這個閉包變量的惟一建議途徑(注意這裏是惟一建議途徑,不是惟一途徑,由於經過getState獲取到的是state的引用,因此是能夠直接修改的。可是這樣就不能更新視圖了)。
正常狀況下咱們只須要像下面這樣this
//action creator var addTodo = function(text){ return { type: 'add_todo', text: text }; }; function TodoReducer(state = [], action){ switch (action.type) { case 'add_todo': return state.concat(action.text); default: return state; } }; // 經過 store.dispatch(action) 來達到修改 state 的目的 // 注意: 在redux裏,惟一可以修改state的方法,就是經過 store.dispatch(action) store.dispatch({type: 'add_todo', text: '讀書'});// 或者下面這樣 // store.dispatch(addTodo('讀書'));
也就是說dispatch接受一個包含type的對象。框架爲咱們提供了一個建立Action的方法bindActionCreators。
下面來看下源碼
// 核心代碼,並經過apply將this綁定起來 function bindActionCreator(actionCreator, dispatch) { return function() { return dispatch(actionCreator.apply(this, arguments)) } } export default function bindActionCreators(actionCreators, dispatch) { // 若是actionCreators是一個函數,則說明只有一個actionCreator,就直接調用bindActionCreator if (typeof actionCreators === 'function') { return bindActionCreator(actionCreators, dispatch) } // 遍歷對象,而後對每一個遍歷項的 actionCreator 生成函數,將函數按照原來的 key 值放到一個對象中,最後返回這個對象 const keys = Object.keys(actionCreators) const boundActionCreators = {} for (let i = 0; i < keys.length; i++) { const key = keys[i] const actionCreator = actionCreators[key] if (typeof actionCreator === 'function') { boundActionCreators[key] = bindActionCreator(actionCreator, dispatch) } } return boundActionCreators }
bindActionCreators的做用就是使用dispatch把action creator包裹起來,這樣咱們就能夠直接調用他們了。這個在日常開發中不經常使用。
最後咱們回頭來看一下以前調到的中間件,
import thunkMiddleware from 'redux-thunk'; // 兩個reducer const todos = (state = INIT.todos, action) => { // .... }; const filterStatus = (state = INIT.filterStatus, action) => { // ... }; const reducers = combineReducers({ todos, filterStatus }); // 這是咱們日常使用時建立store const store = createStore(reducers, state, applyMiddleware(thunkMiddleware));
爲了下文好理解這個放一下redux-thunk的源碼
function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return next(action); }; } const thunk = createThunkMiddleware(); thunk.withExtraArgument = createThunkMiddleware; export default thunk;
能夠看出thunk返回了一個接受({ dispatch, getState })爲參數的函數
下面咱們來看一下applyMiddleware源碼分析
export default function applyMiddleware(...middlewares) { return createStore => (...args) => { const store = createStore(...args) let dispatch = () => { throw new Error( `Dispatching while constructing your middleware is not allowed. ` + `Other middleware would not be applied to this dispatch.` ) } const middlewareAPI = { getState: store.getState, dispatch: (...args) => dispatch(...args) } // 每一個 middleware 都以 middlewareAPI 做爲參數進行注入,返回一個新的鏈。此時的返回值至關於調用 thunkMiddleware 返回的函數: (next) => (action) => {} ,接收一個next做爲其參數 const chain = middlewares.map(middleware => middleware(middlewareAPI)) // 並將鏈代入進 compose 組成一個函數的調用鏈 // compose(...chain) 返回形如(...args) => f(g(h(...args))),f/g/h都是chain中的函數對象。 // 在目前只有 thunkMiddleware 做爲 middlewares 參數的狀況下,將返回 (next) => (action) => {} // 以後以 store.dispatch 做爲參數進行注入注意這裏這裏的store.dispatch是沒有被修改的dispatch他被傳給了next; dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } } // 定義一個代碼組合的方法 // 傳入一些function做爲參數,返回其鏈式調用的形態。例如, // compose(f, g, h) 最終返回 (...args) => f(g(h(...args))) export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } else { const last = funcs[funcs.length - 1] const rest = funcs.slice(0, -1) return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args)) } }
也就是一個三級柯里化的函數,咱們從頭來分析一下這個過程
// createStore.js if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { throw new Error('Expected the enhancer to be a function.') } return enhancer(createStore)(reducer, preloadedState) }
也就是說,會變成這樣
applyMiddleware(thunkMiddleware)(createStore)(reducer, preloadedState)
applyMiddleware接收thunkMiddleware做爲參數,返回形如(createStore) => (...args) => {}的函數。
以 createStore 做爲參數,調用上一步返回的函數(...args) => {}
以(reducer, preloadedState)爲參數進行調用。 在這個函數內部,thunkMiddleware被調用,其做用是監測type是function的action。
所以,若是dispatch的action返回的是一個function,則證實是中間件,則將(dispatch, getState)做爲參數代入其中,進行action 內部下一步的操做。不然的話,認爲只是一個普通的action,將經過next(也就是dispatch)進一步分發。
也就是說,applyMiddleware(thunkMiddleware)做爲enhance,最終起了這樣的做用:
對dispatch調用的action進行檢查,若是action在第一次調用以後返回的是function,則將(dispatch, getState)做爲參數注入到action返回的方法中,不然就正常對action進行分發,這樣一來咱們的中間件就完成了。
所以,當action內部須要獲取state,或者須要進行異步操做,在操做完成以後進行事件調用分發的話,咱們就可讓action 返回一個以(dispatch, getState)爲參數的function而不是一般的Object,enhance就會對其進行檢測以便正確的處理。
到此redux源碼的主要部分學習結束。