React-Redux 原始碼解析 一(createStore)

bluebrid發表於2018-08-26

createStore

一般而言,我檢視一個庫的原始碼,首先回檢視對應方法的引數,其次是對應的return ,然後再看程式碼的具體實現。

通過檢視原始碼,發現createStore 方法返回了一個物件, 該物件共暴露出了五個方法,四個常用的方法:

  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable
  }
複製程式碼

檢視原始碼的開始部分,我們發現createStore可以傳入兩個三個引數:

export default function createStore(reducer, preloadedState, enhancer) 
複製程式碼

其中第一個引數reducer 是必須要傳遞的而且必須是一個函式,不然Redux回報一場

  if (typeof reducer !== 'function') {
    throw new Error('Expected the reducer to be a function.')
  }
複製程式碼

如果傳遞了第二個引數preloadedState,而且第二個引數不是一個function , 則將preloadedState 儲存在內部變數currentState中, 也就是我們給State 的預設狀態

如果preloadedState 是一個function , 則將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, 一般enhancer 就是一組中介軟體
    return enhancer(createStore)(reducer, preloadedState)
  }

  if (typeof reducer !== 'function') {
    throw new Error('Expected the reducer to be a function.')
  }
複製程式碼

該方法中儲存了三個重要的變數:

let currentReducer = reducer
let currentState = preloadedState
let currentListeners = []
複製程式碼
  1. currentReducer 儲存了所有的Reducer
  2. currentState 將狀態資料都儲存在這裡,也是Redux 運算元據的唯一物件
  3. currentListeners 會儲存對Redux State 訂閱的監聽者

我們已經知道了Redux能控制的如上三個主要內部變數了, 接下拉我們會 根據createStore 暴露出來的五個方法,來學習怎麼去操作這三個變數

  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable
  }
複製程式碼

dispatch(去掉驗證程式碼)

  function dispatch(action) {
      try {
      isDispatching = true
      // 呼叫Reduce 對action 進行處理,每次傳入原始的state.返回更新後的states
      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
  }
複製程式碼

首先需要傳遞一個action 引數來告訴需要做的操作物件,action 只能是一個Object, 而且必須包含type 欄位

    if (!isPlainObject(action)) {
      throw new Error(
        'Actions must be plain objects. ' +
        'Use custom middleware for async actions.'
      )
    }

    if (typeof action.type === 'undefined') { // 必須包含**Type**
      throw new Error(
        'Actions may not have an undefined "type" property. ' +
        'Have you misspelled a constant?'
      )
    }
複製程式碼

我們會呼叫內部變數currentReducer 去處理髮起的對應的action

currentState = currentReducer(currentState, action)
複製程式碼

我們會發現currentReducer 其實是一個function, 而且需要兩個引數: currentState , action.

currentReducer 返回的值賦值給currentState, 由createStore 傳入引數的分析得知,preloadedState 只能是要給Object, 所以currentReducer function 返回的是要給Object.

從這一行程式碼我們可以總結得到:

reducer 其實就是一個函式,根據action 對 currentState 進行處理,並且返回新的currentState 的函式

 const listeners = (currentListeners = nextListeners)
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }
複製程式碼

由上面一段程式碼我們可以得知,在更新state , 需要去遍歷執行所有的監聽者(listener),讓監聽者得知state變更的資訊

subscribe(訂閱)

subscribe顧名思義就是訊息訂閱的意思

  function subscribe(listener) {
    let isSubscribed = true

    ensureCanMutateNextListeners()
    nextListeners.push(listener)

    return function unsubscribe() {
      if (!isSubscribed) {
        return
      }   
      isSubscribed = false
      ensureCanMutateNextListeners()
      const index = nextListeners.indexOf(listener)
      nextListeners.splice(index, 1)
    }
  }
複製程式碼

很簡單的一段程式碼, 傳入的引數listener 就是一個訂閱者方法,

 nextListeners.push(listener)
複製程式碼

將listener 儲存在內部變數陣列中。返回一個unsubscribe方法, 用來取消訂閱

nextListeners.splice(index, 1) // 只要從陣列中刪除listener 就是取消訂閱了
複製程式碼

getState(獲取狀態)

  function getState() {
    return currentState
  }
複製程式碼

非常簡單,就是返回內部變數currentState

replaceReducer(替換reducer)

 function replaceReducer(nextReducer) {
    currentReducer = nextReducer
    dispatch({ type: ActionTypes.REPLACE })
  }

複製程式碼

就是替換當前的reducer.

總結

  1. 首先我們需要呼叫createStore 方法建立一個store
 let store = createStore(reducer, preloadedState)
複製程式碼

reducer 是一個function, 而且必須要 傳遞兩個引數,第一個是state, 第二個是一個action

  1. 利用相關事件觸發store.dispatch(action)去變更狀態

這個action 必須要有type 屬性

  1. 訂閱狀態變更store.subscribe(listener)
  2. 在listener中去獲取最新state(store.getState()),然後做去相應的處理

React-Redux 原始碼解析 一(createStore)
可以在Redux examples 下載程式碼執行檢視效果.

相關文章