Redux學習之一:何爲middleware?

接觸到redux有半個月之久了,最初是被各類概念繞的雲裏霧裏,可是最近本身實現了一個demo以後,才發現redux是如此簡潔,簡潔到源代碼就五部分組成,最大的createStore.js也就151行代碼而已。node

createStore.js
applyMiddleware.js
bindActionCreators.js
combineReducers.js
compose.js

redux的魅力在於其簡潔和FP編程思想。像reducers的設計、curry編程風格等都散發出淡淡的FP的味道。
下面言歸正傳,進入middleware部分。下面由幾個問題領着你們看看middleware的真面目。git

1.什麼是middleware?

學過nodejs的人對middleware必定不會陌生。在nodejs裏面,middleware是req和res之間的中間層,能夠作不少事情。但在redux裏面,middleware又是什麼呢。
https://github.com/rackt/redux/blob/master/docs/advanced/Middleware.md
middleware文檔裏面有這樣一句話:github

It provides a third-party extension point between dispatching an
action, and the moment it reaches the reducer.編程

不難理解,在redux裏,middleware是發送action和action到達reducer之間的第三方擴展,也就是中間層。也能夠這樣說,middleware是架在action和store之間的一座橋樑。redux

2.爲何要引入middleware?

也許有人會問,到底middleware有什麼用?
這就要從action提及。在redux裏,action僅僅是攜帶了數據的普通js對象( plain JavaScript objects)。action creator返回的值是這個action類型的對象。而後經過store.dispatch()進行分發……數組

action ---> dispatcher ---> reducers瀏覽器

同步的狀況下一切都很完美……
若是遇到異步狀況,好比點擊一個按鈕,但願2秒以後更新視圖,顯示消息「Hi」。咱們可能這麼寫ActionCreator:app

var asyncSayActionCreator = function (message) {
    setTimeout(function () {
        return {
            type: 'SAY',
            message
        }
    }, 2000)
}

這會報錯,由於這個asyncSayActionCreator返回的不是一個action,而是一個function。這個返回值沒法被reducer識別。
你們可能會想到,這時候須要在action和reducer之間架起一座橋樑……異步

3.middleware如何工做?

咱們看看redux-thunk的代碼:async

export default function thunkMiddleware({ dispatch, getState }) {
  return next => action =>
    typeof action === 'function' ?
      action(dispatch, getState) :
      next(action);
}

僅僅是區區的6行代碼,以致於我第一次看代碼的時候懷疑是否是看錯了,但其實它就這麼簡單。一個三目符,若是action是一個函數,執行這個action函數,若是不是函數,執行next函數。
可能你們換是不懂,結合middleware的應用便會一目瞭然:

const finalCreateStore=applyMiddleware(thunkMiddleware)(createStore)
const store = finalCreateStore(reducer)

這就是咱們最常使用middleware的代碼。把源碼中的next換成createStore,若是action是一個函數(這裏的action是改造後的ActionCreator),便會執行這個action(dispatch, getState)函數。

var asyncSayActionCreator = function (message) {
    return function (dispatch) {
        setTimeout(function () {
            dispatch({
                type: 'SAY',
                message
            })
        }, 2000)
    }
}

這裏的action是return的函數:

function (dispatch) {
        setTimeout(function () {
            dispatch({
                type: 'SAY',
                message
            })
        }, 2000)
    }

若是action返回的不是函數,即返回的是action對象的話,執行createStore函數的dispatch方法。
有了middleware以後,數據流動的方向變爲:

action ---> dispatcher ---> middleware 1 ---> middleware 2 ---> reducers

4.applyMiddleware都作了什麼?

redux2.0對applyMiddleware作了柯里化處理。

import compose from './compose';
export default function applyMiddleware(...middlewares) {
  return (next) => (reducer, initialState) => {
    var store = next(reducer, initialState);
    var dispatch = store.dispatch;
    var chain = [];

    var middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    };
    chain = middlewares.map(middleware => middleware(middlewareAPI));
    dispatch = compose(...chain)(store.dispatch);

    return {
      ...store,
      dispatch
    };
  };
}

其實最核心的代碼也就是下面兩句:

chain = middlewares.map(middleware => middleware(middlewareAPI));
dispatch = compose(...chain)(store.dispatch);

前一句是執行middlewares數組裏的每個中間件,下一句是將chain柯里化,合成一個函數。

5.本身的middleware怎麼寫?

學了這些,咱們能夠本身寫一個middleware練練手。
首先在項目下建個middlewares的文件夾,新建一個callTraceMiddleware.js來追蹤函數的調用過程。
在funCallTrace.js添加以下代碼:

export default function callTraceMiddleware ({dispatch,getState}){
    return next=> action =>{
        console.trace();
        return next(action);
    }
}

而後在調用中間件部分添加中間件:

const createStoreWithMiddleware = applyMiddleware(
  thunkMiddleware,
  loggerMiddleware,
  callTraceMiddleware
)(createStore);

這樣咱們運行在瀏覽器窗口就能夠看到打印的函數調用軌跡。是否是很簡單……

總結

redux的middleware是對action進行擴展處理,這樣豐富了應用需求。以上是我對redux的我的理解,如發現錯誤懇請批評指正。

相關文章
相關標籤/搜索