redux進階 --- 中間件和異步操作


  你為什么需要異步操作? https://stackoverflow.com/questions/34570758/why-do-we-need-middleware-for-async-flow-in-redux  

  在redux基礎篇的介紹中,我們介紹了redux的基本概念, 對於state的改變有了詳盡的了解,但是並沒有提到異步問題如何解決? 何為異步? Action 發出以后,Reducer 立即算出 State,這叫做同步;Action 發出以后,過一段時間再執行 Reducer,這就是異步。 

  在vue中對異步的實現是在actions下使用,如下所示:

 getDefaultAddress ({commit, state}) {
      return new Promise(function (resolve, reject) {
        axios.get('/bbg/user/get_default_address', {
          params: {
            uid: localStorage.getItem("uid")
          }
        }).then(function (response) {
          if (response.data.code == 152) {
            console.log("獲取默認地址成功");
            // 存儲默認地址
            commit(UPDATE_DEFAULT_ADDRESS, response.data.data);
          }
          resolve();
        });
      });
    },

  這里是一個異步的操作,即首先執行getDefaultAddress, 然后在執行這個的過程中,我們需要等到返回結果之后再去改變state數據,而vue的方法就是在判斷成功的使用commit一個reducer,這樣,就可以完成異步的操作了。 

  那么react中是如何實現這種異步操作呢?  這時就需要使用工具: 中間件了。 

  

一、中間件的概念

  為了理解中間件,讓我們站在框架作者的角度思考問題:如果要添加功能,你會在哪個環節添加?

1)Reducer:純函數,只承擔計算 State 的功能,不合適承擔其他功能,也承擔不了,因為理論上,純函數不能進行讀寫操作。
(2)View:與 State 一一對應,可以看作 State 的視覺層,也不合適承擔其他功能。
(3)Action:存放數據的對象,即消息的載體,只能被別人操作,自己不能進行任何操作。

  想來想去,只有發送 Action 的這個步驟,即store.dispatch()方法,可以添加功能。舉例來說,要添加日志功能,把 Action 和 State 打印出來,可以對store.dispatch進行如下改造。

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

  

  上面代碼中,store.dispatch進行了重定義在發送 Action 前后添加了打印功能。這就是中間件的雛形

  中間件就是一個函數,對store.dispatch方法進行了改造,在發出 Action 和執行 Reducer 這兩步之間,添加了其他功能。

 

個人理解:

將具體業務和底層邏輯解耦的組件。

大致的效果是:
需要利用服務的人(前端寫業務的),不需要知道底層邏輯(提供服務的)的具體實現,只要拿着中間件結果來用就好了。

舉個例子:
我開了一家炸雞店(業務端),然而周邊有太多屠雞場(底層),為了成本我肯定想一個個比價,再綜合質量挑選一家屠雞場合作(適配不同底層邏輯)。由於市場變化,合作一段時間后,或許性價比最高的屠雞場就不是我最開始選的了,我又要重新和另一家屠雞場合作,進貨方式、交易方式等等全都要重來一套(重新適配)。

然而我只想好好做炸雞,有性價比高的肉送來就行。於是我找到了一個專門整合屠雞場資源的第三方代理(中間件),跟他談好價格和質量后(統一接口),從今天開始,我就只需要給代理錢,然后拿肉就行。代理負責保證肉的質量,至於如何根據實際性價比,選擇不同的屠雞場,那就是代理做的事了。

 

  即中間件實際上就是在某兩個步驟之間承擔一部分任務,完成某個功能,這就是中間件。 

 

 

  

二、 中間件的用法

  本教程不涉及如何編寫中間件,因為常用的中間件都有現成的,只要引用別人寫好的模塊即可。比如,上一節的日志中間件,就有現成的redux-logger模塊。這里只介紹怎么使用中間件

  

import { applyMiddleware, createStore } from 'redux';
import createLogger from 'redux-logger';
const logger = createLogger();

const store = createStore(
  reducer,
  applyMiddleware(logger)
);

  

  即先從redux中import使用中間件的插件,然后就可以在創建store的時候使用中間件了。 當然,中間件也是需要提前引入的。 

  上面代碼中,redux-logger提供一個生成器createLogger,可以生成日志中間件logger。然后,將它放在applyMiddleware方法之中,傳入createStore方法,就完成了store.dispatch()的功能增強。

 

  補充:我們怎么知道 redux 模塊是否含有 applyMiddleware 和 createStore模塊呢?  我們可以在 npm install redux --save 之后在node-modules中找到redux,然后在redux中找到入口文件,接着進一步找到內層文件,找到 export , 我們在es/utils/index.js中可以看到導出文件如下: 

  export { createStore, combineReducers, bindActionCreators, applyMiddleware, compose };

  這樣,引入applyMiddleware中間件就沒有任何問題了。 

 

  需要注意的是:(1)createStore方法可以接受整個應用的初始狀態作為參數,那樣的話,applyMiddleware就是第三個參數了。

const store = createStore(
  reducer,
  initial_state,
  applyMiddleware(logger)
);

 

  (2)中間件的次序有講究。

const store = createStore(
  reducer,
  applyMiddleware(thunk, promise, logger)
);

上面代碼中,applyMiddleware方法的三個參數,就是三個中間件。有的中間件有次序要求,使用前要查一下文檔。比如,logger就一定要放在最后,否則輸出結果會不正確

 

 

三、applyMiddlewares()

  上面我們使用了applyMiddlewares()方法,這個方法的作用就是使用中間件,之前,我們也已經找到其源碼所在位置,現在粘貼如下所示:

  

export default function applyMiddleware() {
  for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) {
    middlewares[_key] = arguments[_key];
  }

  return function (createStore) {
    return function (reducer, preloadedState, enhancer) {
      var store = createStore(reducer, preloadedState, enhancer);
      var _dispatch = store.dispatch;
      var chain = [];

      var middlewareAPI = {
        getState: store.getState,
        dispatch: function dispatch(action) {
          return _dispatch(action);
        }
      };
      chain = middlewares.map(function (middleware) {
        return middleware(middlewareAPI);
      });
      _dispatch = compose.apply(undefined, chain)(store.dispatch);

      return _extends({}, store, {
        dispatch: _dispatch
      });
    };
  };
}

  即這里可以接受任意多的中間件,然后將所有中間件放在一個middlewares數組中。  接着返回一個函數,使用中間件,在阮一峰老師的博客中也有此段源代碼,寫法有不同之處,只是這里是es5他的是es6的語法。 

  

 

四、異步操作的基本思路

 

  理解了中間件以后,就可以處理異步操作了。

       同步操作只要發出一種 Action 即可,異步操作的差別是它要發出三種 Action

操作發起時的 Action
操作成功時的 Action
操作失敗時的 Action

  以向服務器取出數據為例,三種 Action 可以有兩種不同的寫法。

// 寫法一:名稱相同,參數不同
{ type: 'FETCH_POSTS' }
{ type: 'FETCH_POSTS', status: 'error', error: 'Oops' }
{ type: 'FETCH_POSTS', status: 'success', response: { ... } }

// 寫法二:名稱不同
{ type: 'FETCH_POSTS_REQUEST' }
{ type: 'FETCH_POSTS_FAILURE', error: 'Oops' }
{ type: 'FETCH_POSTS_SUCCESS', response: { ... } }

  除了 Action 種類不同,異步操作的 State 也要進行改造,反映不同的操作狀態。下面是 State 的一個例子。

let state = {
  // ... 
  isFetching: true,
  didInvalidate: true,
  lastUpdated: 'xxxxxxx'
};

 

  

 

五、 redux-thunk 中間件

  這個中間件就可以解決異步actions的多次action觸發問題。

 

 

 

 

六、 redux-promise中間件

  既然 Action Creator 可以返回函數,當然也可以返回其他值。另一種異步操作的解決方案,就是讓 Action Creator 返回一個 Promise 對象。

  即此中間件解決的問題和redux-thunk中間件解決的問題相似。 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM