redux中間件的原理

  • 前言
    react已經出來好久了,其生態圈之龐大,一鍋燉不下!各類react-xx,已讓咱們不堪重負,github上隨便一個demo,引入的模塊至少都是五指之數+。看着頭疼,嚼之無味……。
    在此建議新學者,能夠從基礎的核心模塊學起,前期不要考慮那些數量繁多的馬仔小弟,邊學邊寫,我的感受前期核心要學的流程大體以下:
    React ——> React + redux + React-redux ——> React + redux + React-redux + React-router ——> React + redux + React-redux + React-router ;
    其它的,看狀況學習和了解,我也很菜,以上感悟都是針對初學者,但願能夠減小他們在學習過程當中接觸過多的東西,而影響學習信心和樂趣。
  • 文檔
    React小書(做者從無到有,講述了React的起源,通俗易懂)Note: 第三階段的文檔如今開始收費查看了,不過對於搞前端的人來講不用錢也能夠來個親密接觸的(你們本身想辦法)
    Redux莞式教程(拋開需求講實用性都是耍流氓,做者扮演一位PM給咱們上了生動的一課,深刻淺出,簡明扼要)
    React-Router文檔(一部中規中矩的翻譯之做)

以上是整理的一些說明和文檔資料,沒有看過的能夠去了解一下。下面將開始本文的主題:redux的中間件applyMiddleware。前端

都說名字越長,越讓學者懼怕,applyMiddleware的名字看起來就挺嚇人,那麼爲何會出現中間件,它是作什麼的?它爲何叫中間件?爲何說能夠用來解決異步dispatch?通過一段時間的瞭解,讓我漸漸明白了它的工做原理,如今讓咱們帶問題,懷着簡單,輕鬆的心態走進applyMiddleware大講堂:react

 

  1. 爲何會出現中間件?
    咱們知道redux的核心,就是控制和管理全部的數據輸入輸出,所以有了dispatch,因爲dispatch是一個很純的純函數,就是單純的派發action來更改數據,其功能簡單且固定。
    假如如今產品經理A某有個需求,要求記錄每次的dispatch記錄,咱們怎麼辦呢?最簡單直接的辦法就是在每個dispatch的前面加上:
    console.log('dispatching', action);
    dispatch(action)

     假如又來一個產品B說,我須要記錄每次數據出錯的緣由,咱們怎麼辦呢?而後咱們又須要在對每個dispatch作修改git

    try{
      dispatch(action)    
    }catch(err){
      console.error('錯誤報告: ', err)  
    }  

    若是咱們的程序中有不少的dispatch,咱們就須要添加不少的重複代碼,雖然編輯器提供批量替換,但這無疑是產生了不少樣板代碼。
    由於全部的需求都是和dispatch息息相關,因此只要咱們把日誌放進dispatch函數裏,不就行了嗎,咱們只須要更改dispatch函數,把dispatch進行一層封裝。
    大概的封裝就是下面這樣:
    github

    let next = store.dispatch
    store.dispatch = function dispatchAndLog(action) {
      console.log('dispatching', action)
      next(action)
    }

    Redux把這個封裝的入口寫成了一個函數,就叫applyMiddleware。
    由此咱們明白了applyMiddleware的功能:改造dispatch函數,產生真假dispatch,而中間件就是運行在假真(dispatchAndLog和next)之間的代碼。
    這裏咱們要對applyMiddleware進行一個準確的定義,它只是一個用來加工dispatch的工廠,而要加工什麼樣的dispatch出來,則須要咱們傳入對應的中間件函數(好比上例中的dispatchAndLog),下面咱們構造一個精簡版的applyMiddleware:redux

    const applyMiddleware = function(middleware){
      let next = store.dispatch;
      store.dispatch = middleware(store)(next);  // 這裏傳入store,是由於中間件中有可能會用到getState獲取數據,好比打印當前用戶等需求
    }
    
    applyMiddleware(dispatchAndLog) 

     

  2. 中間件的串聯融合。
    中間件的功能各不相同,它們都要融入到dispatch中,在派發action的時候,按照順序一個個的執行,這是一個費腦經的事情。
    假如如今咱們有兩個中間件 logger和collectError兩個中間件函數,那麼大概的執行順序就是 dispatch——>logger改造——>collectError改造。這裏咱們能看到後面的中間件須要接收到前面改造後的dispatch,
    在前面,咱們是直接修改store.dispatch,如今咱們換一種寫法,讓每個中間件函數,接收一個dispatch,而後返回一個改造後的dispatch,來做爲下一個中間件函數的next,以此類推,用ES6的寫法大概代碼以下:
    const logger = store => next => action => {
      console.log('dispatching', action)
      return next(action)
    }
    
    const collectError = store => next => action => {
      try {
        return next(action)
      } catch (err) {
        console.error('Error!', err)
      }
    }

     而後,咱們改造一下applyMiddleware,來接收一個middlewares數組:數組

    function applyMiddleware(middlewares) {
      middlewares = middlewares.slice()
      middlewares.reverse()
    
      let dispatch = store.dispatch
      middlewares.forEach(middleware =>
        dispatch = middleware(store)(dispatch)
      )
      return Object.assign({}, store, { dispatch })
    }

     

    上面的middleware(store)(dispatch) 就至關因而 const logger = store => next => {},這就是構造後的dispatch,繼續向下傳遞。這裏middlewares.reverse(),進行數組反轉的緣由,是最後構造的dispatch,其實是最早執行的。由於在applyMiddleware串聯的時候,每一箇中間件只是返回一個新的dispatch函數給下一個中間件,實際上這個dispatch並不會執行。只有當咱們在程序中經過store.dispatch(action),真正派發的時候,纔會執行。而此時的dispatch是最後一箇中間件返回的包裝函數。而後依次向前遞推執行。
    咱們拿logger和collectError來講明:

    構造過程:react-router

    let next = store.dispatch;
    let dispatch1 = logger(store)(next); 
    // 這時候的console.log('dispatching', action) 是沒有執行的
    
    let dispatch2 = collectError(store)(dispatch1);
    // 這時候的console.log('Error!', err) 也是沒有執行的
    
    store.dispatch = dispatch2;

    執行過程:app

    store.dispatch(action); //假如咱們程序中派發了某個action
    
    //至關因而下面這樣
    dispatch2(action); //此時執行了 console.log('Error', err)
    
    //因爲collectError中間件中的next是接收的logger返回函數即dispatch1,因此在開始執行
    dispatch1(action); //此時執行了 console.log('dispatching', action)
    
    // 這個例子不太合理,由於錯誤報告是先 try 的 next(action),可是正常的流程是如此。

     

     未完待續……異步

相關文章
相關標籤/搜索