Redux原始碼分析之createStore

IslandZzzz發表於2022-06-29

createStore是一個高階函式,主要作用是完成store的初始化
createStore(reducer,preloadedState,enhancer)

export default function createStore(reducer, preloadedState, enhancer) {

  // 只傳兩個引數並且第二個引數是函式的情況下,將其作為增強函式enhancer, 如createStore(reducer,applyMiddleware(middleWare))
  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.')
    }
    return enhancer(createStore)(reducer, preloadedState)
  }

  let currentReducer = reducer
  let currentState = preloadedState // 通過閉包維持一份createStore的state tree,更新時,current指標將指向dispatch生成的new state tree,  通過getStateapi對外暴露最新的state tree
  let currentListeners = []  // subscribe方法收集的訂閱
  let nextListeners = currentListeners  // 用於呼叫ensureCanMutateNextListeners 淺拷貝一份list,防止使用者在dispatching時呼叫subscribe/unsubscribe出現bug
  let isDispatching = false // 是否正在更新state
 
  function dispatch(action) {
    try {
      isDispatching = true
      // 改變當前狀態
      currentState = currentReducer(currentState, action)
    } finally {
      isDispatching = false
    }
    // 在dispatch改變當前狀態之後立即執行所有通過subscribe的函式,執行listener主要用於在更新狀態之後做些什麼事情,比如檢視的render操作,我們可以根據最新的狀態去渲染檢視
    // 這種方式有一個缺點就是,不論你消費的資料有沒有變化,只要你使用subscribe訂閱了store,都會執行訂閱函式
    // 也就是說,redux並不識別具體的訂閱者,而是統一廣播通知,但這一功能被react-redux實現了
    const listeners = (currentListeners = nextListeners)
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }
    return action
  }
  

  dispatch({ type: ActionTypes.INIT }) // 執行createStore完成狀態初始化的時候,會在內部呼叫dispatch傳遞一個type為init的action, 並且返回一個store物件, 這也是唯一的一次非使用者生成的action排程
  
  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable
  }
}

createStore的幾個主要api

  • dispatch
  • subscribe/unsubscribe
  • getState

dispatch

負責響應使用者的動作,派發action給reducer從而獲取最新的檢視狀態

dispatch在初始化的時候由redux自身呼叫一次init action,其他時候都是使用者由派發action呼叫

流程:

  1. dispatch將store的currentState指標指向reducer返回的new state
  2. 向使用者廣播通過subscribe註冊的所有訂閱
  3. 通過getState向使用者暴露currentState
  function dispatch(action) {

    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
  }

subscribe

store.subscribe(listener)
訂閱redux 狀態變化,一旦狀態發生變化就執行所有的訂閱函式,同時返回一個取消訂閱的函式unsubscribe

  function subscribe(listener) {
    let isSubscribed = true
    nextListeners.push(listener)
    return function unsubscribe() {
      if (!isSubscribed) {
        return
      }
      isSubscribed = false
      const index = nextListeners.indexOf(listener)
      nextListeners.splice(index, 1)
      currentListeners = null
    }
  }

getState

createStore通過閉包維持一份state tree。
狀態更新時, currentState指標將指向dispatch生成的new state tree, 並通過getState向外暴露

  function getState() {
    if (isDispatching) {
      throw new Error(
        'You may not call store.getState() while the reducer is executing. ' +
          'The reducer has already received the state as an argument. ' +
          'Pass it down from the top reducer instead of reading it from the store.'
      )
    }

    return currentState
  }

相關文章