Redux源碼(六) —— createStore.js

Source Time

import $$observable from 'symbol-observable'
import ActionTypes from './utils/actionTypes'
import isPlainObject from './utils/isPlainObject'

export default function createStore(reducer, preloadedState, enhancer) {
  // 第一個參數爲reducer,函數類型
  // 第二個參數應爲初始state,類型任意
  // 第三個參數爲enhancer,函數類型,即applyMiddleware返回的函數
  if (
    (typeof preloadedState === 'function' && typeof enhancer === 'function') ||
    (typeof enhancer === 'function' && typeof arguments[3] === 'function')
  ) {
    // 若preloadedState和enhancer均爲函數,
    // 或者參數個數多餘三個且最後兩個都是函數類型,
    // 猜想使用了多個enhancer,拋出錯誤
    throw new Error(
      'It looks like you are passing several store enhancers to ' +
        'createStore(). This is not supported. Instead, compose them ' +
        'together to a single function'
    )
  }

  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    // 若preloadedState爲函數類型,且enhancer爲空,
    // 猜想無初始state,第二個參數直接爲enhancer
    enhancer = preloadedState
    preloadedState = undefined
  }

  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      // 若enhancer存在但不是函數類型,則拋出錯誤,提示enhancer應該是一個函數
      throw new Error('Expected the enhancer to be a function.')
    }

    // 若enhancer存在而且是函數類型,調用enhancer對遞納入參createStore
    // 這裏能夠結合applyMiddleware理解,enhancer即爲applyMiddleware的返回結果
    return enhancer(createStore)(reducer, preloadedState)
  }

  if (typeof reducer !== 'function') {
    // 判斷reducer類型,非函數類型就報錯
    throw new Error('Expected the reducer to be a function.')
  }

  // 當前reducer
  let currentReducer = reducer
  // 當前state
  let currentState = preloadedState
  // 當前事件監聽數組
  let currentListeners = []
  // 下一輪事件監聽數組,此處與ensureCanMutateNextListeners相關聯
  let nextListeners = currentListeners
  // 判斷是否正在dispatch的標誌
  let isDispatching = false

  function ensureCanMutateNextListeners() {/* ... */}
  function getState() {/* ... */}
  function subscribe(listener) {/* ... */}
  function dispatch(action) {/* ... */}
  function replaceReducer(nextReducer) {/* */}
  function observable() {/* ... */}

  // When a store is created, an "INIT" action is dispatched so that every
  // reducer returns their initial state. This effectively populates
  // the initial state tree.
  dispatch({ type: ActionTypes.INIT })

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

Analysis

因爲createStore內部比較大,因此這裏我將一些內部定義的函數拎出單獨描述做用,對於其餘的部分可參考中文註釋內容。最後的英文註釋也很好的描述了在建立store以後經過dispatch一次名字叫做INIT的action來進行整個store的內部state初始化。總結一下非函數部份內部的功能就是如下幾點內容:javascript

  1. 判斷入參是否符合要求,即只能最多一個enhancer
  2. 兼容第二個參數爲enhancer而沒有初始state的狀況
  3. 處理有enhancer的建立邏輯
  4. 判斷reducer的正確性
  5. 定義內部功能變量
  6. 定義內部功能函數或提供給用戶的API
  7. 初始化store,初始化state
  8. 導出API

講完了非函數部份內容,接下來一個一個分析一下在createStore中定義的函數。java

ensureCanMutateNextListeners

function ensureCanMutateNextListeners() {
  if (nextListeners === currentListeners) {
    nextListeners = currentListeners.slice()
  }
}
複製代碼

因爲nextListeners變量定義的時候是根據currentListeners得到的數組引用(參考定義部分),因此爲了避免影響當前事件監聽數組,函數ensureCanMutateNextListeners會在須要更改nextListeners的時候先判斷一下是不是當前事件監聽數組的引用,如果,則會用slice方法得到一個一樣元素的不一樣數組做爲新的nextListenersredux

getState

function getState() {
  // 獲取當前store中state,若是正在dispatch則報錯
  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
}
複製代碼

函數getState主要用於返回當前store中的state對象,須要注意的是若是當前store正在進行dispatch操做,那麼就不能獲取state,而是拋出一個錯誤提示。api

subscribe

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)
  }
}
複製代碼

這裏的subscribe函數主要用於添加用戶事件的監聽,會在dispatch更新state後進行調用(詳情在dispatch部分說明),即將監聽事件加入到nextListeners。須要注意的是,該函數返回的是一個用於解除監聽的unsubscribe方法,這裏利用的是閉包的經典用法,能夠參考學習一下。數組

dispatch

function dispatch(action) {
  if (!isPlainObject(action)) {
    // 判斷是不是符合要求的plain object
    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?'
    )
  }

  if (isDispatching) {
    // 利用isDispatching判斷當前是否正在進行dipatch操做
    throw new Error('Reducers may not dispatch actions.')
  }
  // 更新標示,並利用當前reducer更新state
  try {
    isDispatching = true
    currentState = currentReducer(currentState, action)
  } finally {
    isDispatching = false
  }

  // 經過nextListeners得到最新的當前事件監聽數組
  const listeners = (currentListeners = nextListeners)
  for (let i = 0; i < listeners.length; i++) {
    // 遍歷觸發監聽事件
    const listener = listeners[i]
    listener()
  }

  // 返回入參action
  return action
}
複製代碼

dispatch函數能夠說是用戶接觸最多的一個api了,功能很是強大,可是它的實現倒是很是好理解的。閉包

  1. 首先是對接受的入參——action進行類型校驗,判斷是不是合法的plain object;
  2. 其次是判斷這個action是否包含標示更新類型的type屬性;
  3. 利用isDispatching判斷是否正在進行dispatch操做,若是是則拋出錯誤;
  4. 更新isDispatching標誌,並利用將action做爲入參傳入currentReducer獲得最新的當前state;
  5. 經過nextListeners獲取最新的事件監聽數組,同時遍歷觸發每一個監聽事件;
  6. 返回入參action備用。

replaceReducer

function replaceReducer(nextReducer) {
  if (typeof nextReducer !== 'function') {
    // 判斷nextReducer是不是符合要求的函數類型
    throw new Error('Expected the nextReducer to be a function.')
  }
  // 更新當前reducer爲最新的reducer
  currentReducer = nextReducer
  // 觸發一次REPLACE類型的action用於使用最新的reducer更新當前store中state數據
  dispatch({ type: ActionTypes.REPLACE })
}
複製代碼

replaceReducer函數接受一個新的reducer函數——nextReducer做爲入參,在內部替換掉currentReducer,同時主動dispatch一次REPLACE類型的私有action,用於應用最新的reducer方法更新state。從個人項目經從來說,這個replaceReducer方法以及接下來的observable方法,使用頻率都不是過高,不知道具體使用場景會是什麼樣子以及其餘人的狀況如何。app

observable

function observable() {
  // 根據subscribe方法定義outerSubscribe方法,備用
  const outerSubscribe = subscribe
  // 返回一個包含subscribe方法的對象
  return {
    /** * The minimal observable subscription method. * @param {Object} observer Any object that can be used as an observer. * The observer object should have a `next` method. * @returns {subscription} An object with an `unsubscribe` method that can * be used to unsubscribe the observable from the store, and prevent further * emission of values from the observable. */
    subscribe(observer) {
      // 接受一個對象做爲觀察者observer
      if (typeof observer !== 'object' || observer === null) {
        // 校驗observer類型
        throw new TypeError('Expected the observer to be an object.')
      }
      // 定義一個監聽state的方法
      function observeState() {
        if (observer.next) {
          // 運行observer對象的next方法,以當前store的state做爲入參
          observer.next(getState())
        }
      }
      // 執行一次observerState方法
      observeState()
      // 定義解除監聽的方法,並做爲一個對象的屬性,返回該對象
      const unsubscribe = outerSubscribe(observeState)
      return { unsubscribe }
    },
    // 獲取當前對象的this指向
    [$$observable]() {
      return this
    }
  }
}
複製代碼

坦白說,以前歷來沒有接觸、使用過這個api,因此對於其做用知之甚少,暫時只能經過代碼層面來解讀其做用,後面能夠進一步瞭解一下。async

All

相關文章
相關標籤/搜索