第一次看源碼,並無想象中的難哈,主要是redux的源碼比較少,理解起來也比較簡單。看過以後,感受更深刻的理解了redux思想和函數式編程的理念,建議你們能夠去看一下嘻嘻,看完以後確定會有收穫的。
javascript
我是對照着網上別人看過的源碼筆記看的,寫這篇文章的緣由呢,是想總結一下,由於我記性比較差啦,算是作一個筆記,方便之後複習。
java
redux的源碼中,有6個js文件,分別是:react
咱們一個一個來分析吧~編程
這裏呢沒有太多須要講的,就是暴露了5個核心的api,分別是:redux
首先,定義初始化的actionapi
export const ActionTypes = { INIT: '@@redux/INIT' }
這個createStore函數,會傳入三個參數和一個返回值,分別是:
數組
一、 @param {Function} reducer
app
這個reducer是一個函數,這個函數接收state和action,做一系列計算以後返回一個新的state。這裏就體現了函數式編程的一些特性:
async
第一,這個reducer是一個純函數,純函數的特色是:對於相同的輸入,永遠會獲得相同的輸出,並且沒有任何可觀察的反作用,也不依賴外部環境的狀態。不理解純函數的筒子們,能夠上網搜索一下。
ide
第二,state是不可變的,咱們這裏對state做的一些修改和計算,不是直接修改原來的數據,而是返回修改以後的數據,原來的數據是保持不變。這裏能夠衍生到immutable,可使用immutable和redux搭配使用。
二、@param {any} [preloadedState]
這是初始化的state,很少說。
三、@param {Function} [enhancer]
這個enhancer其實就是一箇中間件,它在redux3.1.0以後才加入的。至關於把store作一些加強處理,讓store更強大,功能更豐富,在以後的applyMiddleware那裏會詳細說的。這裏也體現了高階函數的思想,就像react-redux的connect方法同樣,作一些包裝處理以後,再返回。
四、@returns {Store}
這是返回的值,返回的是一棵狀態樹,也就是store啦。
這是作的源碼分析,都寫在註釋裏了。createStore返回的最經常使用的三個api是dispatch,subscribe,getState,通常咱們只要傳入reducer和preloadedState,就能夠直接調用這三個方法,很是方便。
export default function createStore(reducer, preloadedState, enhancer) { //這裏是一些參數校驗 //若是第二個參數爲函數且沒有傳入第三個參數,那就交換第二個參數和第三個參數 //意思是createSotre會認爲你忽略了preloadedState,而傳入了一個enhancer if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') { enhancer = preloadedState preloadedState = undefined } if (typeof enhancer !== 'undefined') { //若是傳入了第三個參數,但不是一個函數,就報錯 if (typeof enhancer !== 'function') { throw new Error('Expected the enhancer to be a function.') } //這是一個高階函數調用方法。這裏的enhancer就是applyMiddleware(...middlewares) //enhancer接受createStore做爲參數,對createStore的能力進行加強,並返回加強後的createStore //而後再將reducer和preloadedState做爲參數傳給加強後的createStore,獲得最終生成的store return enhancer(createStore)(reducer, preloadedState) } //reducer不是函數,報錯 if (typeof reducer !== 'function') { throw new Error('Expected the reducer to be a function.') } //聲明一些變量 let currentReducer = reducer //當前的reducer函數 let currentState = preloadedState//當前的狀態樹 let currentListeners = [] // 當前的監聽器列表 let nextListeners = currentListeners //更新後的監聽器列表 let isDispatching = false //是否正在dispatch //判斷當前listener和更新後的listener是否是同一個引用,若是是的話對當前listener進行一個拷貝,防止在操做新的listener列表的時候對正在發生的業務邏輯形成影響 function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { nextListeners = currentListeners.slice() } } /** * * @returns {any} The current state tree of your application. */ //返回當前的狀態樹 function getState() { return currentState } /** *這個函數是給store添加監聽函數,把listener做爲一個參數傳入, *註冊監聽這個函數以後,subscribe方法會返回一個unsubscribe()方法,來註銷剛纔添加的監聽函數 * @param {Function} listener 傳入一個監聽器函數 * @returns {Function} */ function subscribe(listener) { if (typeof listener !== 'function') { throw new Error('Expected listener to be a function.') } //註冊監聽 let isSubscribed = true ensureCanMutateNextListeners() //將監聽器壓進nextListeners隊列中 nextListeners.push(listener) //註冊監聽以後,要返回一個取消監聽的函數 return function unsubscribe() { //若是已經取消監聽了,就返回 if (!isSubscribed) { return } //取消監聽 isSubscribed = false //在nextListeners中找到這個監聽器,而且刪除 ensureCanMutateNextListeners() const index = nextListeners.indexOf(listener) nextListeners.splice(index, 1) } } /** * @param {Object} action 傳入一個action對象 * * @returns {Object} */ function dispatch(action) { //校驗action是否爲一個原生js對象 if (!isPlainObject(action)) { throw new Error( 'Actions must be plain objects. ' + 'Use custom middleware for async actions.' ) } //校驗action是否包含type對象 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.') } //設置正在派發的標誌位,而後將當前的state和action傳給當前的reducer,用於生成新的state //這就是reducer的工做過程,純函數接受state和action,再返回一個新的state try { isDispatching = true currentState = currentReducer(currentState, action) } finally { isDispatching = false } //獲得新的state以後,遍歷當前的監聽列表,依次調用全部的監聽函數,通知狀態的變動 //這裏沒有把最新的狀態做爲參數傳給監聽函數,是由於能夠直接調用store.getState()方法拿到最新的狀態 const listeners = currentListeners = nextListeners for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener() } //返回action return action } /** *這個方法主要用於reducer的熱替換 * @param {Function} nextReducer * @returns {void} */ function replaceReducer(nextReducer) { if (typeof nextReducer !== 'function') { throw new Error('Expected the nextReducer to be a function.') } // 把傳入的nextReducer給當前的reducer currentReducer = nextReducer //dispatch一個初始action dispatch({ type: ActionTypes.INIT }) } /** * 用於提供觀察者模式的操做,貌似是一個預留的方法,暫時沒看到有啥用 * @returns {observable} A minimal observable of state changes. */ function observable() { const outerSubscribe = subscribe return { /** * The minimal observable subscription method. * @param {Object} observer * 觀察者應該有next方法 * @returns {subscription} */ subscribe(observer) { //觀察者模式的鏈式結構,傳入當前的state if (typeof observer !== 'object') { throw new TypeError('Expected the observer to be an object.') } //獲取觀察者的狀態 function observeState() { if (observer.next) { observer.next(getState()) } } observeState() //返回一個取消訂閱的方法 const unsubscribe = outerSubscribe(observeState) return { unsubscribe } }, [$$observable]() { return this } } } //初始化一個action dispatch({ type: ActionTypes.INIT }) return { dispatch, subscribe, getState, replaceReducer, [$$observable]: observable } }
combineReducers的做用是將以前切分的多個子reducer合併成一個大的reducer,也就是說將不少的小狀態樹合併到一棵樹上,整合成一棵完整的狀態樹。
這個函數接受一個參數,返回一個函數
一、@param {Object} reducers
這裏接收多個reducer,傳入的是一個對象
二、@returns {Function}
combineReducers的整個執行過程就是:將全部符合標準的reducer放進一個對象中,當dispatch一個action的時候,就遍歷每一個reducer,來計算出每一個reducer的state值。同時,每遍歷一個reducer,就判斷新舊state是否發生改變,來決定是返回新state仍是舊state,這是作的一個優化處理。
源碼分析以下,前面還有一部分是一些error信息和warning信息的處理,就沒有放進來了,感興趣的話能夠本身去看一下完整的源碼。
export default function combineReducers(reducers) { //獲取reducers的全部key值 const reducerKeys = Object.keys(reducers) //最終生成的reducer對象 const finalReducers = {} for (let i = 0; i < reducerKeys.length; i++) { const key = reducerKeys[i] if (process.env.NODE_ENV !== 'production') { if (typeof reducers[key] === 'undefined') { warning(`No reducer provided for key "${key}"`) } } //遍歷reducer,把key值都是function的reducer放進finalReducers對象中 if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key] } } //獲得finalReducers的key值數組 const finalReducerKeys = Object.keys(finalReducers) let unexpectedKeyCache if (process.env.NODE_ENV !== 'production') { unexpectedKeyCache = {} } //檢測這些reducer是否符合標準 let shapeAssertionError try { //檢測是不是redux規定的reducer形式 assertReducerShape(finalReducers) } catch (e) { shapeAssertionError = e } //計算state的邏輯部分 return function combination(state = {}, action) { if (shapeAssertionError) { throw shapeAssertionError } //若是不是production(線上)環境,作一些警告 if (process.env.NODE_ENV !== 'production') { const warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache) if (warningMessage) { warning(warningMessage) } } //標誌state是否改變 let hasChanged = false //存儲新的state const nextState = {} for (let i = 0; i < finalReducerKeys.length; i++) { //遍歷finalReducerKeys的key值,也就是reducer的名字 const key = finalReducerKeys[i] //獲得reducer的vlaue值 const reducer = finalReducers[key] //變化前的state值 const previousStateForKey = state[key] //變化後的state值,把變化前的state和action傳進去,計算出新的state const nextStateForKey = reducer(previousStateForKey, action) //若是沒有返回新的reducer,就拋出異常 if (typeof nextStateForKey === 'undefined') { const errorMessage = getUndefinedStateErrorMessage(key, action) throw new Error(errorMessage) } //把變化後的state存入nextState數組中 nextState[key] = nextStateForKey //判斷state是否有改變 hasChanged = hasChanged || nextStateForKey !== previousStateForKey } //若是改變了state就返回新的state,沒改變就返回原來的state return hasChanged ? nextState : state } }
bindActionCreators的做用是:將action與dispatch函數綁定,生成能夠直接觸發action的函數。
//使用dispatch包裝actionCreator方法 function bindActionCreator(actionCreator, dispatch) { return (...args) => dispatch(actionCreator(...args)) } /* * @param {Function|Object} actionCreators * * @param {Function} dispatch * * @returns {Function|Object} * */ export default function bindActionCreators(actionCreators, dispatch) { //actionCreators爲函數,就直接調用bindActionCreator進行包裝 if (typeof actionCreators === 'function') { return bindActionCreator(actionCreators, dispatch) } if (typeof actionCreators !== 'object' || actionCreators === null) { throw new Error( `bindActionCreators expected an object or a function, instead received ${actionCreators === null ? 'null' : typeof actionCreators}. ` + `Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?` ) } //如下是actionCreators爲對象時的操做 //遍歷actionCreators對象的key值 const keys = Object.keys(actionCreators) //存儲dispatch和actionCreator綁定以後的集合 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 }
compose叫作函數組合,是一個柯里化函數,將多個函數合併成一個函數,從右到左執行。這同時也是函數式編程的特性。這個函數會在applyMiddleware中用到
/** * @param {...Function} funcs The functions to compose. * @returns {Function} A function obtained by composing the argument functions * from right to left. For example, compose(f, g, h) is identical to doing * (...args) => f(g(h(...args))). */ export default function compose(...funcs) { //判斷傳入參數的數量,作不一樣的處理 if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args) => a(b(...args))) }
這一段代碼的主要難點是在最後那一句,着重說一下reduce這個方法。這個reduce不是以前的reducer,這裏的reduce函數是es5的一個歸併數組的方法,是從數組的第一項開始,逐個遍歷數組的全部項。
它接收兩個參數,一個是在每一項上調用的函數,還有一個可選參數,是做爲歸併基礎的初始值。調用的那個函數又接收四個參數,前一個值,當前值,項的索引,和數組對象。這個函數返回的任何值都會做爲第一個參數自動傳遞給下一項。這樣說可能比較抽象,舉個例子:
[1,2,3,4,5].reduce((prev, cur) => { return prev + cur //輸出15 })
用reduce就能夠很快的求的數組全部值相加的和。另外,還有一個reduceRight()方法,跟reduce是同樣的,只不過是從數組的右邊開始遍歷的。
咱們回到源碼上面return funcs.reduce((a, b) => (...args) => a(b(...args)))
,這其實就是遍歷傳入的參數數組(函數),將這些函數合併成一個函數,從右到左的執行。這就是中間件的創造過程,把store用一個函數包裝以後,又用另外一個函數包裝,就造成了這種包菜式的函數。
applyMiddleware是用來擴展redux功能的,主要就是擴展store.dispatch的功能,像logger、redux-thunk就是一些中間件。
它的實現過程是:在dispatch的時候,會按照在applyMiddleware時傳入的中間件順序,依次執行。最後返回一個通過許多中間件包裝以後的store.dispatch方法。
若是理解了以前說的compose函數,這一段代碼應該也很容易就能看懂啦。
/** * @param {...Function} middlewares 接收不定數量的中間件函數 * @returns {Function} 返回一個通過中間件包裝以後的store */ export default function applyMiddleware(...middlewares) { //返回一個參數爲createStore的匿名函數 return (createStore) => (reducer, preloadedState, enhancer) => { //生成store const store = createStore(reducer, preloadedState, enhancer) //獲得dispatch方法 let dispatch = store.dispatch //定義中間件的chain let chain = [] //在中間件中要用到的兩個方法 const middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) } //把這兩個api給中間件包裝一次 chain = middlewares.map(middleware => middleware(middlewareAPI)) //鏈式調用每個中間件,給dispatch進行封裝,再返回最後包裝以後的dispatch dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } }
整個的源碼就所有分析完了,咱們能夠看到,redux的源碼不少地方都體現了函數式編程的思想。函數式編程寫出來的代碼確實很漂亮很簡潔,可是理解起來也比較困難。這也只是函數式編程的很小一部分,有興趣的話能夠去了解一下其餘的部分。
寫到這裏也差很少了,但願之後有機會能多看點源碼,get一些新的知識,最後感謝宋老師的寶貴意見,bye