redux的中間件applyMiddleware源碼

原文: redux的applyMiddleware源碼

記得之前第一次看redux源碼的時(shí)候是很懵逼的,尤其是看到applyMiddleware函數(shù)的時(shí)候,更是懵逼。當(dāng)然那也是半年前的事情了,前幾天把redux源碼看了下,并且實(shí)現(xiàn)了個(gè)簡(jiǎn)單的redux功能。但是沒有實(shí)現(xiàn)中間件。今天突然又想看看redux的中間件,就看了起來(lái)。

記得半年之前是從函數(shù)聲明的下一行就開始看不懂了。。。然后前段時(shí)間,看了下柯里化函數(shù),加深了高階函數(shù)的印象,所以今天打算把中間件的源碼給擼一下。

我們來(lái)看看函數(shù)聲明的下一行,也就是源碼第二行開始看:

export default function applyMiddleware(...middlewares) {
  return createStore => (...args) => {
    const store = createStore(...args)
    let dispatch = () => {
      throw new Error(
        `Dispatching while constructing your middleware is not allowed. ` +
          `Other middleware would not be applied to this dispatch.`
      )
    }

    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    }
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

從上面我們可以看到中間件返回了函數(shù),返回第一個(gè)函數(shù)是攜帶createStore參數(shù)的,這個(gè)是啥?從名字上我們就可以知道,就是createStore。不過為了證明,我們還是得從源碼上來(lái)看。

還記得是怎么調(diào)用的中間件的吧,大致如下:

const store = createStore(
    reducer,
    applyMiddleware(...middlewares)
);

可以看到中間件是在createStore參數(shù)里調(diào)用的(在參數(shù)里運(yùn)行函數(shù),導(dǎo)致傳遞給createStore的是中間件運(yùn)行后返回的結(jié)果,從上面的中間件源碼可以知道,返回的就是攜帶createStore參數(shù)的函數(shù)),現(xiàn)在我們可以進(jìn)createStore函數(shù)里看看他是怎么處理中間件返回的函數(shù)的。

redux的主要實(shí)現(xiàn)都是在createStore里實(shí)現(xiàn)的,所以我們主要看createStore里處理參數(shù)的部分:

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)
  }

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

  //other code
}

從我們調(diào)用createStore可以知道第一個(gè)參數(shù)是reducer,第二個(gè)參數(shù)就是中間件運(yùn)行之后返回的攜帶createStore參數(shù)的函數(shù)。但是在上面的這段源碼里,我們發(fā)現(xiàn)是preloadedState來(lái)接收這個(gè)攜帶createStore參數(shù)的函數(shù),感覺不是很多,命名的'不好'。先繼續(xù)往下看,wow, 是一個(gè)判斷,他會(huì)判斷preloadedState是不是一個(gè)函數(shù),第三個(gè)參數(shù)enhancer是不是未定義;如果preloadedState是函數(shù),enhancer是未定義,那么就會(huì)把preloadedState賦值給enhancer,并且設(shè)置preloadedState是未定義。 這樣就沒有問題了,在這里,相當(dāng)于第三個(gè)參數(shù)enhancer接收了攜帶createStore參數(shù)的函數(shù)。

然后第二個(gè)判斷:

if (typeof enhancer !== 'undefined') {
  if (typeof enhancer !== 'function') {
    throw new Error('Expected the enhancer to be a function.')
  }

  return enhancer(createStore)(reducer, preloadedState)
}

他會(huì)去運(yùn)行這個(gè)enhancer。這個(gè)enhancer是什么?就是我們說(shuō)的攜帶createStore的函數(shù)。

有意思的是,這個(gè)enhancer直接在這里運(yùn)行了,并且采用了createStore作為參數(shù)(這個(gè)createStore就是函數(shù)呀)。 我們?cè)賮?lái)看看enhancer(createStore)返回的是啥:

return function (...args){
      const store = createStore(...args)
      let dispatch = () => {
        throw new Error(
          `Dispatching while constructing your middleware is not allowed. ` +
          `Other middleware would not be applied to this dispatch.`
        )
      }

      const middlewareAPI = {
        getState: store.getState,
        dispatch: (...args) => dispatch(...args)
      }
      const chain = middlewares.map(middleware => middleware(middlewareAPI))
      dispatch = compose(...chain)(store.dispatch)

      return {
        ...store,
        dispatch
      }
    }

有意思,返回的是帶有多個(gè)參數(shù)的函數(shù)。
上面的代碼相當(dāng)于:

enhancer(createStore) ~= function(...args) => function(reducer, preloadedState)

可以看到,上面的(...args)就是相當(dāng)于(reducer, preloadedState)。

那么我們?cè)賮?lái)看看上面的function(...args), 額, 直接在第一行就再次調(diào)用創(chuàng)建store,這樣不會(huì)陷入無(wú)限循環(huán)嗎?不會(huì),因?yàn)橛袇?shù)判斷,在createStore的原方法里不會(huì)再執(zhí)行enhancer; 所以我們可以發(fā)現(xiàn),在有中間件的時(shí)候,真正的執(zhí)行createStore是在中間件里去執(zhí)行的,并且攜帶的參數(shù)是reducer, preloadedState

所以上面第一行創(chuàng)建了個(gè)store對(duì)象,他返回的屬性有:

{
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable
  }

然后新建了個(gè)指向dispatch變量的匿名函數(shù),這個(gè)函數(shù)在調(diào)用的時(shí)候拋出異常告訴你不可以在構(gòu)造中間件時(shí)調(diào)用dispatch。

Dispatching while constructing your middleware is not allowed. Other middleware would not be applied to this dispatch.

接下來(lái)會(huì)創(chuàng)建一個(gè)middlewareAPI對(duì)象:

const middlewareAPI = {
    getState: store.getState, //獲取store里的state
    dispatch: (...args) => dispatch(...args) // 調(diào)用`dispatch`的時(shí)候會(huì)拋錯(cuò),如果在組合中間件之前調(diào)用,下面會(huì)說(shuō)
}

一開始我以為是在調(diào)用的時(shí)候就會(huì)報(bào)錯(cuò),可是發(fā)現(xiàn)這個(gè)對(duì)象里的dispatch攜帶參數(shù),如果只是單純拋錯(cuò),完全可以不需要傳遞參數(shù),然后向下看下去才看到其中的奧妙。

然后就是對(duì)中間件集合middlewares(數(shù)組)進(jìn)行操作:

const chain = middlewares.map(middleware => middleware(middlewareAPI)) //返回了新的集合,對(duì)應(yīng)的每個(gè)中間件調(diào)用的結(jié)果

然后就是組合這些中間件了, 這里對(duì)高階函數(shù)不熟的,可以看下柯里化函數(shù)和函數(shù)組合::

// china是上面返回的中間件的結(jié)果
dispatch = compose(...chain)(store.dispatch)

可以看到這個(gè)代碼,組合了中間件, 使用compose這個(gè)高階函數(shù)來(lái)處理的。我們看下這個(gè)高階函數(shù):

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)))
}

上面的代碼比較有意思:

  • 第一個(gè)判斷
    如果沒有中間件作為參數(shù)傳遞,那么直接返回一個(gè)函數(shù),接收對(duì)應(yīng)的參數(shù)并且返回這個(gè)參數(shù)。
  • 第二個(gè)判斷
    如果如果這個(gè)中間件參數(shù)只有一個(gè),那么直接返回這個(gè)中間件函數(shù)
  • 最后一步
    那就是多個(gè)中間件傳遞進(jìn)來(lái)的時(shí)候,他會(huì)借用reduce方法組合(這個(gè)放在后面), 會(huì)有個(gè)...args參數(shù),就是(store.dispatch),等下回說(shuō)到。
    可能你對(duì)reduce不是很熟,可以簡(jiǎn)單的看下他干了什么事:
['1', 2, 3, 'n'].reduce((a, b) => console.log('a is',a , 'b is', b)) // 這樣你就會(huì)發(fā)現(xiàn)這個(gè)方法在這里的作用

其實(shí)從注釋里也可以知道:

* @param {...Function} funcs The functions to compose.
* @returns {Function} A function obtained by composing the argument functions
* from right to left. For example, compose(f, g, h) is identical to doing
* (...args) => f(g(h(...args))).

把這些中間件都執(zhí)行到dispatch.

再回到上面看compose的返回:

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

我們?cè)倏纯粗虚g件調(diào)用compose的地方:

dispatch = compose(...chain)(store.dispatch)

從這個(gè)地方再配合看compose(...chain) => result的這個(gè)result.

  • 第一個(gè)判斷
    返回的是(arg) => arg就是相當(dāng)于 result(arg) => arg, 果然,直接返回這個(gè)store.dispatch
  • 第二個(gè)判斷
    返回的是唯一的一個(gè)中間件result. 然后中間件直接調(diào)用store.dispatch作為參數(shù)。
  • 最后一個(gè)
    這個(gè)返回的是一個(gè)函數(shù),看起來(lái)像這樣:
(...args) => a(b(...args))

這樣就相當(dāng)于result(args) => a(b(...args)),這樣就保證每個(gè)中間件都會(huì)用到dispatch,并且最終返回這個(gè)被擴(kuò)展過的dispatch.

然后可以看到中間件函數(shù)返回了對(duì)象:

{
  ...store,
  dispatch
}

這個(gè)dispatch就是被處理過的dispatch。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 在學(xué)習(xí)了redux過程中,了解到中間件這個(gè)名詞,但是我看了十遍,也完全就是懵逼的狀態(tài)。于是又重復(fù)敲了幾次代碼也不能...
    綽號(hào)陸拾柒閱讀 600評(píng)論 0 0
  • “中間件”這個(gè)詞聽起來(lái)很恐怖,但它實(shí)際一點(diǎn)都不難。想更好的了解中間件的方法就是看一下那些已經(jīng)實(shí)現(xiàn)了的中間件是怎么工...
    Jmingzi_閱讀 1,782評(píng)論 1 7
  • export const ActionTypes = {INIT:'@@redux/INIT'} // 生成一個(gè)s...
    jiandan5850閱讀 554評(píng)論 0 0
  • 前天,我急匆匆的做作業(yè),突然我發(fā)現(xiàn)一張?jiān)嚲聿灰娏?,但又不敢告訴媽媽,就想留到晚上媽媽檢查作業(yè)的時(shí)候再說(shuō),晚上媽媽看...
    露露空間閱讀 902評(píng)論 2 2
  • 愛情是不是蠢的? 王爾德曾說(shuō):人生就是一件蠢事接著另一件蠢事,而愛情就是兩個(gè)蠢東西互相追來(lái)追去。一點(diǎn)無(wú)緣無(wú)故的好感...
    趣讀書吧閱讀 3,012評(píng)論 0 1

友情鏈接更多精彩內(nèi)容