Redux原始碼分析(一)

jinjiaxing發表於2019-04-19

Redux原始碼分析(createStore)

使用redux都快3年了,到現在也沒認真去了解一下原始碼罪過啊,所以需要對它進行一些分析和學習,一方面能更好的去使用它,另一方面也學習一下該框架的設計思路,首先我們看到 redux/src/index.js 檔案

export {
  createStore,
  combineReducers,
  bindActionCreators,
  applyMiddleware,
  compose,
  __DO_NOT_USE__ActionTypes
}
複製程式碼

所以主要部分也就是上面幾個函式,我們下面一點點去分析每一個功能點


createStore

其實該檔案有著大量的註釋了,大家可以先簡單的看看程式碼中的註釋瞭解一遍。 其實createStore就是返回了一個物件,這個物件只有幾個方法而已,而我們常用的就是dispatchsubscribegetState這三個了

 * 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/…

相關文章