Redux API 和 Redux 源碼結構

Redux API

export {
    createStore,             //創建一個state用來存儲狀態樹
    combineReducers,   //合併reducer
    bindActionCreators,  //將dispatch和action結合
    applyMiddleware,     //調度中間件來增強store,例如中間件redux-thunk等
    compose              //從右向左組合多個函數, compose(f, g, h)會返回(...args) => f(g(h(...args)))
}

源碼結構

Redux源碼結構和提供的API大體對應,如下:
utils——warning.js //console中打印warning信息要用到的
applyMiddleware.js 
bindActionCreators.js
combineReducers.js
compose.js
createStore.js
index.js //export 上述定義的module

createStore

上面我們看到了redux的API和源碼結構,看的出來,warning.js和index.js不用解析,都看得懂,關鍵時其餘的幾個module,那我們從最重要的createStore講起。

    export var ActionTypes = {
        INIT: '@@redux/INIT'
    }
   //首先定義了一個action類型,我們知道更新state的唯一方法就是dispatch一個action,這個action是用
   // 來初始化state的,後面會用到它

現在來看下createStore的大體結構

//接收三個參數
//reducer爲function。當dispatch一個action時,此函數接收action來更新state
//preloadState初始化State
//enhancer 爲function。用來增強store, Redux 定義有applyMiddleware來增強store,後面會
//單獨講applyMiddleware

export default function createStore(reducer, preloadedState, enhancer) {
    if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
        //如果只傳了兩個參數,並且第二個參數爲函數,第二個參數會被當作enhancer
        enhancer = preloadedState
        preloadedState = undefined
    }
  
    if (typeof enhancer !== 'undefined') {
        if (typeof enhancer !== 'function') {
            //校驗enhancer是否爲函數,如果不是函數則拋出異常
            throw new Error('Expected the enhancer to be a function.')
        }
        //如果enhancer存在且爲函數,那麼則返回如下調用,如果enhancer爲applyMiddleware,那麼調用則
        //是applyMiddleware(createStore)(reducer, preloadedState)。後面講applyMiddleware再詳細講。
        return enhancer(createStore)(reducer, preloadedState)
    }

    if (typeof reducer !== 'function') {
        //校驗reducer是否爲函數
        throw new Error('Expected the reducer to be a function.')
    }

    var currentReducer = reducer
    //得到reducer

    var currentState = preloadedState
    //得到初始init,沒有傳遞則爲undefined

    var currentListeners = []
    //定義一個數組用來存放listeners。就是一個函數數組,當state發生改變時,會循環執行這個數組裏面的函數

    var nextListeners = currentListeners
    //用來存儲下一次的listeners數組。爲什麼要有這個listeners數組呢?因爲當state發生改變時,我們根據
    //上面的currentListeners來循環執行函數,但是在這執行這些函數時,函數內部可能取消或者添加訂閱
    //(state改變時,添加或者取消執行函數),這時如果直接操作currentListeners ,相當於在循環
    //內部修改循環條件,執行瞬間就亂套了,有沒有啊,有沒有

    var isDispatching = false
    //reducer函數是否正在執行的標識

    function ensureCanMutateNextListeners() {
        //拷貝currentListeners一份爲nextListeners,這樣nextListeners的改變不會引起currentListeners的改變
        //(上面解釋過原因啦)
    }

    function dispatch() {
        //觸發action去執行reducer,更新state
        .....
    }

    function subscribe() {
        //接收一個函數參數,訂閱state的改變。當state改變時會執行這個函數
        ....
    }

    function getState() {
        //獲取state樹
        ....
    }

    function replaceReducer() {
        //替換reducer
        ....
    }

    function observable() {
        //沒用,不解釋(後面有解釋)
        ......
    }


    dispatch({ type: ActionTypes.INIT })
    //執行dispatch函數,初始化state

    return {
        //真正的返回,執行createStore其實返回的就是這些東東
        dispatch,       //觸發action去執行reducer,更新state
        subscribe,     //訂閱state改變,state改變時會執行subscribe的參數(自己定義的一個函數)
        getState,      //獲取state樹
        replaceReducer,       //替換reducer
        [$$observable]: observable         
        //redux內部用的,對我們來說沒用(非要深究它寫這是幹嘛的?咋跟
        //我一樣死腦筋呢,都說了沒用啦。算了,還是告訴你把,就是內部用的,在測試代碼中會用到,感興
        //趣的可以去test目錄下查看)
    }
}

現在是不是感覺明朗(懵逼)了許多,懵逼就對了,接下來我們再來解析一下dispatch, subscribe等函數的具體實現,或許會讓你明朗(更懵逼)起來 
看了上面的大體結構,我們明白以下這些就夠了。
createStore是一個函數,它定義了一些變量(currentState, currentListeners等)及函數(dispatch, subscribe等),並且調用了dispatch,最後返回一個對象,該對象包含的就是dispatch和subscribe等函數。接下來我們來解析這些函數。
createStore裏面只調用了一個函數,那就是dispatch,那我們就從這裏開始講起。

dispatch

它是這樣被調用的,有沒有很熟悉。。。

dispatch({ type: ActionTypes.INIT })

來看看dispatch的源碼

  function dispatch(action) {
    //校驗action參數,必須爲一個純粹的對象。這也說明了,我們不能直接在redux做異步請求,而是需要
    //使用applyMiddleware去應用一些中間件。比如redux-thunk等
    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?'
      )
    }
    //判斷是否正在執行reducers函數,如果正在執行,此action不會觸發,那有人就問題了,那是不是我
    //的dispatch不會起作用,導致state沒有更新,數據是錯誤的? 答案是state是不會有錯。因爲redux本
    //身整個更新state的過程是同步的,從dipatch——>reducers——>state。所以這段代碼意在你定義的
    //reducers函數中不允許調用dispatch
    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }

    try {
      //設置標識爲,並且執行currentReducer,還記得嗎?這個我們通過參數獲取到的一個函數,往往是我
      //們調用combineReducers返回,combineReducers我們後面解析
      isDispatching = true
      //調用reducers函數
      currentState = currentReducer(currentState, action)
    } finally {
      isDispatching = false
    }
    //調用所有訂閱state改變的函數,這些函數就可以通過getState函數獲取到最新的state值。訂閱的函數
    //從哪裏來呢,從subscribe中來, 我們後面來解析subscribe和getState
    var listeners = currentListeners = nextListeners
    for (var i = 0; i < listeners.length; i++) {
      listeners[i]()
    }

    return action
  }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章