redux中間件源碼解析

它提供了一個(gè)分類處理action的機(jī)會(huì)。在中間件中,可以檢閱每個(gè)流過(guò)的action,挑選出特定類型的action進(jìn)行相應(yīng)的操作,提供一次改變action的機(jī)會(huì)。

通過(guò)中間件,我們可以在dispatch action之后,到達(dá)reducer之前,搞點(diǎn)事情。比如:打印,報(bào)錯(cuò),跟異步API通信等等

下面,讓我們一步步來(lái)理解下中間件是如何實(shí)現(xiàn)的:
我們從createStore.js調(diào)用中間件的地方開(kāi)始理解:

export default function createStore(reducer, preloadedState, enhancer) {
// 如果第二個(gè)參數(shù)沒(méi)有傳入初始的state,而是傳入了enhancer
// applyMiddleware調(diào)用的返回值, 那就將第二個(gè)參數(shù),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.')
    }
    // 當(dāng)enhancer不為空且為函數(shù)時(shí),就執(zhí)行該函數(shù),并return回去,作為creatStore的返回值。
    return enhancer(createStore)(reducer, preloadedState)
  }

理解middleware機(jī)制

redux提供了applyMiddleware方法來(lái)加載中間件,先來(lái)看下總覽:

export default function applyMiddleware(...middlewares) {
  return createStore => (...args) => {
    const store = createStore(...args)
    ....
    ....
    ....
    return {
      ...store,
      dispatch
    }
  }

applyMiddleware接受不定數(shù)量的中間件,然后返回一個(gè)接受一個(gè)creatStore作為參數(shù)的函數(shù)的函數(shù)?;厝ピ倏聪挛覀兊?code>creatStore:

if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }
    // 當(dāng)enhancer不為空且為函數(shù)時(shí),就執(zhí)行該函數(shù),并return回去,作為creatStore的返回值。
    return enhancer(createStore)(reducer, preloadedState)
  }

當(dāng)enhancer不為空且為函數(shù)時(shí),就執(zhí)行該函數(shù),并return回去,作為creatStore的返回值。回憶下我們?cè)趓edux中怎樣使用中間件的

const store = createStore(reducers, applyMiddleware(a, b, c));

其實(shí)enhancer就是applyMiddleware的結(jié)果。也就是createStore => (...args) => {}

export default function applyMiddleware(...middlewares) {
  return createStore => (...args) => {
    const store = createStore(...args)
    // 聲明一個(gè)dispatch
    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)
    }
    // 第一步把middlewares里的函數(shù)帶著middlewareAPI這個(gè)參數(shù)都挨個(gè)執(zhí)行,
    //然后保存在chain數(shù)組中。也就是middleware中的第一個(gè)參數(shù),返回 next => action => {...}
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    // 問(wèn)題:compose是如何將中間件串聯(lián)在一起的?
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

compose.js怎么組合函數(shù)的呢?

return funcs.reduce((a, b) => (...args) => a(b(...args)))
一臉蒙蔽,這是什么?
現(xiàn)在假設(shè)我們需要組合兩個(gè)函數(shù):

var f1 = function(a) {
    return a * 10;
}

var f2 = function(a) {
    return a + 1;
}

function compose(f1, f2) {
    return function(x) {
        return f1(f2(x))
    }
}

var number = compose(f1, f2)(4) 

讓代碼從右向左運(yùn)行變成f1(f2(x)),4 -> f2 -> f1 -> 50
現(xiàn)在我們修改下compose函數(shù)

let compose = (...funs) => (result) => {
    for (var i = funs.length - 1; i > -1; i--) {
      result = funs[i].call(this, result);
    }
    
    return result;
}
var number = compose(f1, f2)(4)

我們?cè)倩氐絩edux的compose.js
return funcs.reduce((a, b) => (...args) => a(b(...args)))
拆分上面的源碼步驟如下:

  1. 返回 (...args) => a(b(...args)且與下一個(gè)c函數(shù)做計(jì)算
  2. a 等于 (...args) => a(b(...args) ,b 等于 c 這個(gè)function , 然后再一次做 (...args) => a(b(...args))計(jì)算,就形成了(c(...args)) => a(b(...args))返回 a(b(c(...args)))
  3. 所以compose其實(shí)就是實(shí)現(xiàn)了組合運(yùn)算(...args) => f(g(h(...args))).

再回頭看下applyMiddleware的方法:

export default function applyMiddleware(...middlewares) {
  return createStore => (...args) => {
    const store = createStore(...args)
    // 聲明一個(gè)dispatch
    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)
    }
    // 第一步把middlewares里的函數(shù)帶著middlewareAPI這個(gè)參數(shù)都挨個(gè)執(zhí)行,
    //然后保存在chain數(shù)組中。也就是middleware中的第一個(gè)參數(shù),返回 next => action => {...}
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    // 在中間件中調(diào)用dispatch的時(shí)候, 其實(shí)就是調(diào)用了這個(gè)dispatch,而不是一開(kāi)始聲明的邏輯
    // 這個(gè)dispatch是已經(jīng)經(jīng)過(guò)compose的包裝的函數(shù)
    // const composeRes = compose(...chain);
    // 第二行通過(guò)傳入store.dispatch,
    // 這個(gè)store.dispatch就是最后一個(gè) next => action => {}的next參數(shù)
    // 所以中間件其實(shí)就是改裝dispatch
    // dispatch作為有中間件的store的dispatch屬性輸出
    //  當(dāng)用戶調(diào)用dispatch時(shí), 中間件就會(huì)一個(gè)一個(gè)
    // 執(zhí)行完邏輯后, 將執(zhí)行權(quán)給下一個(gè), 直到原始的store.dispacth, 最后計(jì)算出新的state
    // 問(wèn)題:compose是如何將中間件串聯(lián)在一起的?
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

總結(jié)下:

  1. 第一步把middlewares數(shù)組里的函數(shù)帶著middlewareAPI這個(gè)參數(shù)都挨個(gè)執(zhí)行個(gè)遍且,且保存在chain數(shù)組中。也就是middleware中的第一個(gè)參數(shù),返回 next => action => {...}
  2. 就是上面composeJs 中組合chain中的函數(shù)。變成f1(f2(f3(store.dispatch)))
    并且最終保存在dispatch 變量中。里面的函數(shù)最終會(huì)返回action => {...}這個(gè)函數(shù),
    作為上一個(gè)函數(shù)的next參數(shù)保存在上個(gè)函數(shù)的scope中,所以每個(gè)middleware 中必須執(zhí)行next(action)才能進(jìn)入下個(gè)middleware。
    而最后一個(gè)middleware傳入的next正是原始的store.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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • export const ActionTypes = {INIT:'@@redux/INIT'} // 生成一個(gè)s...
    jiandan5850閱讀 554評(píng)論 0 0
  • “中間件”這個(gè)詞聽(tīng)起來(lái)很恐怖,但它實(shí)際一點(diǎn)都不難。想更好的了解中間件的方法就是看一下那些已經(jīng)實(shí)現(xiàn)了的中間件是怎么工...
    Jmingzi_閱讀 1,782評(píng)論 1 7
  • 原文: redux的applyMiddleware源碼 記得之前第一次看redux源碼的時(shí)候是很懵逼的,尤其是看到...
    xiaohesong閱讀 409評(píng)論 0 0
  • 前言 最近幾天對(duì) redux 的中間件進(jìn)行了一番梳理,又看了 redux-saga 的文檔,和 redux-thu...
    Srtian閱讀 33,702評(píng)論 9 40
  • 孤單是一個(gè)人的狂歡,狂歡是一群人的孤單
    Flowery閱讀 210評(píng)論 0 0

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