redux中間件

前言

提到中間件,你可能會想到ExpressKoa等服務端框架,沒想到也不要緊,這句話是我裝逼用的。javascript

那麼redux中的中間件到底幹嗎用的?java

有這樣一個問題?咱們以前用的Redux都是在Action發出以後當即執行Reducer,計算出state,這是同步操做。若是想異步操做呢?即過一段時間再執行Reducer怎麼辦?這裏就須要用到中間件middlewareredux

先放一張圖看看:數組

1、中間件的概念

redux是有流程的,那麼,咱們該把這個異步操做放在哪一個環節比較合適呢?promise

  • Reducer?純函數只承擔計算State功能,不適合其它功能。
  • View?與State一一對應,能夠看作是State的視覺層,也不適合承擔其它功能。
  • Action?它是一個對象,即存儲動做的載體,只能被操做。

其實,也只有dispatch能勝任此重任了。那麼怎麼在dispatch中添加其它操做呢?bash

let next = store.dispatch;
store.dispatch = function(action){
   console.log('老狀態 ',store.getState());
   next(action);
   console.log('新狀態 ',store.getState());
}
複製代碼

示例中能夠看出,咱們對store.dispatch從新進行了定義,在發送action的先後,作了打印。app

這是中間件的大體雛形,真實的中間件要比這麼複雜多了框架

2、中間件的用法

咱們在這裏先看看中間件是怎麼使用,下面咱們一步步剖析每一個細節。dom

import {applyMiddleware,createStore} from 'redux';
import reduxLogger form 'redux-logger';

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

複製代碼

代碼中有兩點須要注意:異步

  • 一、createStore方法能夠整個應用的初始狀態做爲參數 內部是這麼處理的
let state = inital_state;
複製代碼
  • 二、中間件的參數次序有講究。下面我會把這個問題講明白。

3、applyMiddleware

Middleware可讓你包裝storedispatch方法來達到你想要的目的。同時,middleWare還擁有「可組合」這一關鍵特性。多個middleWare能夠被組合到一塊兒使用,造成middleWare鏈,依次執行。其中每一個middleware不須要關心鏈先後的的middleWare的任何信息。

function applyMiddleware(...middlewares){
    return function(createStore){
        return function(reducer){
            //引入store
            let store = createStore(reducer);
            let dispatch = store.dispatch;
            let middlewareAPI = {
                getState:store.getState,
                // 對dispatch進行包裝
                dispatch:action=>dispatch(action)
            }
            //每一箇中間件都是這種模型  ({ getState, dispatch }) => next => action
            chain = middlewares.map(middleware=>middleware(middleAPI));
            dispatch = compose(...chain)(store.dispatch);
            // dispatch被改裝後,返回store
            return{...store,dispatch};
        }
    }
}
複製代碼

上面代碼中,全部中間件都被放進了一個數組chain,而後嵌套執行,最後執行store.dispatch。中間件內部middlewaAPI能夠拿到getStatedispatch這兩個方法。

...middleware:遵循Redux middleware API的函數。每一個middleware接受StoredispatchgetState函數做爲命名參數,並返回一個函數。該函數會被傳入成爲next的下一個middleWare 的dispatch方法,並返回一個接收action的新函數,這個函數能夠直接調用next(action),或者在其餘須要的時刻調用,甚至根本不去調用它。

因此,接下來,咱們就能看到middleware的函數簽名是({ getState, dispatch }) => next => action

其實,它的本質就是包裝sotre中的dispatch

上面代碼中,還用到了compose方法,咱們來看看compose是怎麼是實現的?

compose

先看下面一個栗子:

function add1(str){
   return str+1;
}
function add2(str){
    return str+2;
 }
 function add3(str){
    return str+3;
 }
 let result = add3(add2(add1('好吃')));// 好吃123;
 
複製代碼

這中寫法調用起來,一層套一層,是否是看着很不爽,咱們簡化一下:

function compose(...fns){
    if(fns.length==1)
     return fns[0];
   return function(...args){
    let last = fns.pop();
    return fns.reduceRight((prev,next)=>{
         return  next(prev);  
    },last(...args));
   }
 }
 let add = compose(add3,add2,add1);//
 let result = add('好吃');// 好吃123
 // 上面的代碼其實就是redux3.6.0版本中compose的實現方式
複製代碼

看看這個代碼是否是用起來,很乾練一些。其實還能夠簡化

function compose(...fns){
  if(fns.length==1)
     return fns[0];
   return fns.reduce((a,b)=>(...args)=>a(b(...args)));//add3(add2(add1('好吃')))
 }
 let add = compose(add3,add2,add1);//
 let result = add('好吃');// 好吃123
 // 這是redux3.6.0版本以後的compose實現方式,一直沿用至今。
複製代碼

至於爲何applyMiddleWare的參數有順序,這裏給出了答案。

4、Applymiddleware的三個經常使用參數

4.一、日誌記錄

使用 Redux 的一個益處就是它讓 state 的變化過程變的可預知和透明。每當一個 action 發起完成後,新的 state 就會被計算並保存下來。State 不能被自身修改,只能由特定的 action 引發變化。

試想一下,當咱們的應用中每個 action 被髮起以及每次新的 state 被計算完成時都將它們記錄下來,豈不是很好?當程序出現問題時,咱們能夠經過查閱日誌找出是哪一個 action 致使了 state 不正確。

圖片的效果是否是很期待啊!!!

咱們先來手動實現一版。

// 記錄全部被髮起的action和新的state
let next = store.dispatch;
store.dispatch = function(action){
   console.log('老狀態 ',store.getState());
   next(action);
   console.log('新狀態 ',store.getState());
}
複製代碼

仍是上面的示例,咱們來作個修改

let logger = function({ getState, dispatch }){
   return function(next){// 這裏的next能夠理解爲store.dispath,本質上就是調用 middleware 鏈中下一個 middleware 的 dispatch。
      return function(action){
        console.log('老狀態1 ',getState());
        next(action);//派發動做
        console.log('新狀態1 ',getState());
    }
    }
}
// 高逼格寫法
let logger = ({ getState, dispatch }) => next => action => {
  console.log('老狀態1 ',getState());
  next(action)
  console.log('新狀態1 ',getState());
}
複製代碼

4.二、redux-thunk 中間件

redux-thunkredux官方文檔中用到的異步組件,實質就是一個redux中間件,一個封裝表達式的函數,封裝的目的就是延遲執行表達式。

redux-thunk是一個通用的解決方案,其核心思想是讓action能夠變成一個thunk,這樣的話,同步狀況:dispatch(action),異步狀況:dispatch(thunk)

下面是redux-thunk的實現:

let thunk = ({dispatch,getState})=>next=>action=>{
    if(typeof action == 'function'){
        action(dispatch,getState);
    }else{
        next(action);//這裏能夠理解爲dispatch(action),本質上就是調用 middleware 鏈中下一個 middleware 的 dispatch。
    }
}
複製代碼

使用redux-thunk

const store = createStore(  
  reducer,
  applyMiddleware(thunk)
);
複製代碼

而後咱們實現一個thunkActionCreator

//過一秒加1
    export function thunkActionCreator(payload){
        return function(dispatch,getState){
            setTimeout(function(){
                dispatch({type:types.INCREMENT,payload:payload});
            },1000);
        }
    },
複製代碼

最後,在組件中dispatch thunk

this.dispatch(thunkActionCreator(payload));
複製代碼

4.三、redux-promise

redux-promise也是延遲執行的表達式,它是解決異步的另一種方案。

redux-thunk和核心思想是把action變成thunk,而redux-promise的核心思想是讓action返回一個promise對象。

這個中間件使得store.dispatch方法能夠接收Promise對象做爲參數。這時 ,action 有兩種寫法:

寫法1、返回值是一個Promise對象。

function promiseIncrement(payload){
 // return {type:types.INCREMENT,payload:payload} 之前是這種寫法
    return new Promise(function(resolve,reject){
      setTimeout(function(){
        resolve({type:types.INCREMENT,payload:payload});
      },1000);
    });
 },
複製代碼

寫法二,action 對象的payload屬性是一個Promise對象,這須要從

function payloadIncrement(){
    return {
        type:types.INCREMENT,
        payload: new Promise(function(resolve,reject){
            setTimeout(function(){
                if(Math.random()>.5){
                    resolve(100);
                }else{
                    reject(-100);
                }
            },1000)
        })
    }
}
複製代碼

下面咱們來看看 redux-promise是怎麼實現的,就會明白它內部是怎麼操做的.

let promise = ({dispatch,getState})=>next=>action=>{
    if(action.then && typeof action.then == 'function'){
        action.then(dispatch);
        // 這裏的dispatch就是一個函數,dispatch(action){state:reducer(state,action)};
    }else if(action.payload&& action.payload.then&& typeof action.payload.then == 'function'){
        action.payload.then(payload=>dispatch({...action,payload}),payload=>dispatch({...action,payload}));
    }else{
        next(action);
    }
}
複製代碼

上面的代碼能夠看出,若是Action自己就是一個Promise,它resolve之後的值應該是一個Action對象,會被dispatch方法送出action.then(dispatch);若是Action對象的 payload屬性是一個Promise對象,那麼不管resolvereject,dispatch 方法都會發出Action

相關文章
相關標籤/搜索