初步解讀 Redux 源碼理解其原理

Redux github 地址

前言

Redux 源碼的體量算是比較小的了,但是詳細(xì)解讀還是有點(diǎn)壓力,在此簡單解讀下,目的是能大概理解其原理。直接看導(dǎo)出的 API:createStorecombineReducersbindActionCreatorsapplyMiddlewarecompose,其中 bindActionCreators 暫時(shí)不想理會,余下的依次介紹。

createStore

關(guān)鍵代碼(省去一些加強(qiáng)健壯性的代碼)以及對應(yīng)的解讀注釋如下:

// 函數(shù)接受三個(gè)參數(shù),第一個(gè)是 reducer,第二個(gè)是初始 state,第三個(gè)是由 applyMiddleware
// 生成的 enhancer
export default function createStore(reducer, 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.')
    }

    return enhancer(createStore)(reducer, preloadedState) // 可以看出 enhancer
    // 是一個(gè)以 createStore 為參數(shù)然后返回一個(gè) createStore 的高階函數(shù),待會兒通過
    // applyMiddleware 可以看到這個(gè) enhancer 到底是怎么起作用的
  }
  let currentReducer = reducer
  let currentState = preloadedState
  let currentListeners = []
  let nextListeners = currentListeners
  let isDispatching = false

  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      nextListeners = currentListeners.slice()
    }
  }

  function getState() { // 返回當(dāng)前的 state
    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
  }

  function subscribe(listener) { // 訂閱函數(shù),每當(dāng) dispatch 的時(shí)候 listeners 都會執(zhí)行
    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)

    // 返回值是一個(gè)可以取消當(dāng)前 listener 訂閱的函數(shù)
    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)
    }
  }

  // dispatch,通過這個(gè)函數(shù) dispatch action 借由 reducers 來生成一個(gè)新的 state
  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++) { // 如前所述,每 dispatch 一個(gè) action 時(shí)
      // 都會去執(zhí)行所有的 listeners
      const listener = listeners[i]
      listener()
    }

    return action // 返回值和傳入的參數(shù)一樣,action
  }

  function replaceReducer(nextReducer) { // 替換 reducer
    if (typeof nextReducer !== 'function') {
      throw new Error('Expected the nextReducer to be a function.')
    }

    currentReducer = nextReducer
    // replace reducer 后 dispatch  一個(gè) REPLACE action
    dispatch({ type: ActionTypes.REPLACE })
  }

  // 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.
  // store 生成后 dispatch 一個(gè) INIT 的 action
  dispatch({ type: ActionTypes.INIT })

  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer
  }
}

createStore 主要的作用就是根據(jù)提供的 reducerinitialStateenhancer 生成 store,然后 store 可以提供 dispatchsubscribegetStatereplaceReducer等方法。

combineReducers

combineReducer 的作用是將多個(gè)(如果有的話) reducer 整合成一個(gè)總的 reducer,關(guān)鍵代碼:

// reducers 是一個(gè) plain object,一個(gè) key 對應(yīng)一個(gè) reducer
export default function combineReducers(reducers) {
  const reducerKeys = Object.keys(reducers)
  const finalReducers = {}
  for (let i = 0; i < reducerKeys.length; i++) {
    const key = reducerKeys[i]

    // 過濾無效的 reducer
    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key]
    }
  }
  const finalReducerKeys = Object.keys(finalReducers)

  // 返回值依然是一個(gè)以 state 和 action 為參數(shù)的 reducer,但是它可以處理所有的 type 的 action
  return function combination(state = {}, action) {

    let hasChanged = false
    const nextState = {}
    // 具體處理 action 的做法是,把每個(gè) action 代入所有 reducer 生成對應(yīng)的結(jié)果,然后再整合
    for (let i = 0; i < finalReducerKeys.length; i++) {
      const key = finalReducerKeys[i]
      const reducer = finalReducers[key]
      const previousStateForKey = state[key]
      const nextStateForKey = reducer(previousStateForKey, action)

      nextState[key] = nextStateForKey
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    return hasChanged ? nextState : state
  }
}

compose

這個(gè)方法的代碼最少:

export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

funcs是一個(gè)函數(shù)數(shù)組,reduce是數(shù)組的歸并方法。這個(gè)方法接受一系列的函數(shù)作為參數(shù),而且這一系列函數(shù),從右到左,上一個(gè)函數(shù)的返回值可以為下一個(gè)函數(shù)的參數(shù),最終返回一個(gè)以最右邊的函數(shù)的參數(shù)為參數(shù)(這個(gè)參數(shù)再依次交給左邊的函數(shù)處理,返回后繼續(xù)此步驟,一直到最左邊)以最左邊的函數(shù)的返回值為返回值的復(fù)合函數(shù)。比如:

 const F = compose(f1, f2. f3, f4, ..., fn)
 F //f1(f2...(fn(...args)))

applyMiddleware

applyMiddleware,可以說這個(gè)方法為 Redux 提供了各種可能性,關(guān)鍵代碼:

import compose from './compose'

export default function applyMiddleware(...middlewares) {
  // 返回值是一個(gè)同時(shí)以 createStore 為參數(shù)和返回值的閉包
  return createStore => (...args) => {
    const store = createStore(...args) // 這里的store與沒有enhancer時(shí)的并無二致
    let dispatch = () => {
      throw new Error( // 中間件中不允許 dispatch
        `Dispatching while constructing your middleware is not allowed. ` +
          `Other middleware would not be applied to this dispatch.`
      )
    }
    
    // 中間件API,規(guī)定了 middleware 是一個(gè)以 { getState, dispatch } 為參數(shù)的函數(shù)
    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    }

    // chain 的每個(gè)元素依然是一個(gè)函數(shù),經(jīng)過 compose 作用后返回一個(gè)合成函數(shù),合成函數(shù)以
    // store.dispatch 為參數(shù),最終生成一個(gè)加強(qiáng)了的 dispatch
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)
    // 上面語句分開寫就是 const composedFn = compose(...chain)
    // dispatch = composedFn(store.dispatch) 
    // 其中 composedFn: (...args) => chain[0](chain[1](...(chain[length - 1](...args))))
    // 可以看到,`createStore` 經(jīng)過 `storeEnhancer` 加強(qiáng)之后,其實(shí)只是用新的`dispatch` 將原來
    // 的 `dispatch` 替換,其他的部分保持不變
    return {
      ...store,
      dispatch
    }
  }
}

這個(gè)方法接受一系列的 Redux 的 middleware 為參數(shù),然后返回一個(gè)以 createStore 為參數(shù)的 storeEnhancer,其實(shí) storeEnhancer enhance 的是 dispatch(這里好像并不準(zhǔn)確,因?yàn)槌擞芍虚g件生成的 storeEnhancer 以外,還有其他的 storeEnhancer,而這些 storeEnhancer 就有更強(qiáng)的功能,比如像 devToolsExtension 這樣的擴(kuò)展工具)。由于每個(gè) middleware 在作用 { getState, dispatch } 后可以被 compose 處理,那我們可以知道 middleware({ getState, dispatch }) 的返回值是一個(gè)函數(shù),而且這個(gè)函數(shù)的參數(shù)和返回值是具有相同簽名的函數(shù),于是 middleware 的函數(shù)簽名大概是:({ getState, dispatch }) => next => action,其中 next(action) 表示將 action 交由下一個(gè) middleware 處理,最后一個(gè) middleware 的 nextdispatch

舉個(gè)例子:

//m1, m2, m3 是三個(gè)中間件
const middlewares = [m1, m2, m3]

const storeEnhancer = applyMiddleWare(...middlewares)
const store = createStore(reducer, {}, storeEnhancer)

export default store

store.dispatch 被強(qiáng)化的過程是這樣:
普通 dispatch -> 被 m3 強(qiáng)化后的 dispatch(記為 m3(dispatch)) -> 再被 m2 強(qiáng)化后的 dispatch(記為 m2(m3(dispatch))) -> 再被 m1 強(qiáng)化后的 dispatch(記為 m1(m2(m3(dispatch)))
對應(yīng)地,加載了上述中間件的 store dispatch 一個(gè) action 的過程是這樣:
m1(m2(m3(dispatch)))(action) -> next(action)next === m2(m3(dispatch)))-> next(action)next === m3(dispacth))-> next(action)next === dispatch)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 在學(xué)習(xí)了redux過程中,了解到中間件這個(gè)名詞,但是我看了十遍,也完全就是懵逼的狀態(tài)。于是又重復(fù)敲了幾次代碼也不能...
    綽號陸拾柒閱讀 544評論 0 0
  • 看到這篇文章build an image gallery using redux saga,覺得寫的不錯(cuò),長短也適...
    smartphp閱讀 6,214評論 1 29
  • 前言 本文 有配套視頻,可以酌情觀看。 文中內(nèi)容因各人理解不同,可能會有所偏差,歡迎朋友們聯(lián)系我討論。 文中所有內(nèi)...
    珍此良辰閱讀 11,947評論 23 111
  • 上周六參加了一個(gè)公司的面試,因?yàn)槭呛荛L時(shí)間以來的第一次面試,發(fā)揮的并不是很好,有兩個(gè)下來一想就明白的問題在當(dāng)時(shí)卻卡...
    夏爾先生閱讀 6,428評論 0 15
  • 很久以前,一座非常茂密的森林里,住著一只貓頭鷹。它不知道為了什么,竟開始為別人操心。 因此,它開始沉思獅子憑它的權(quán)...
    我要不停的奔跑閱讀 670評論 0 0