理解Redux底層原理從applyMiddleware開始

來源:互聯網
上載者:User

題外話:年前最後一個工作日事情不多,正好把近期看過的但是容易忘的重要知識點總結一下,畢竟好記性不如爛筆頭,不再少年不扶不行。

先放一個Redux中文文檔關於Middleware(中介軟體)的官方解釋。

Redux Middleware

Middleware這個概念是Redux從其他架構借鑒過來的,本意如下:

middleware是指可以被嵌入在架構接收請求到產生響應過程之中的代碼。例如,Express 或者 Koa 的 middleware 可以完成添加 CORS headers、記錄日誌、內容壓縮等工作。

而在Redux中:

middleware被用於解決不同的問題,但其中的概念是類似的。它提供的是位於 action 被發起之後,到達 reducer 之前的擴充點。 你可以利用 Redux middleware 來進行日誌記錄、建立崩潰報告、調用非同步介面或者路由等等。

以上兩段引用均來自官方文檔。 可以看出Redux中的middleware和其他架構中要做的事情大同小異。本質上是切面編程 Aspect Oriented Programming(AOP)的一種思想。商務邏輯和其他部分(日誌,錯誤報表等)可以實現一個良好的解藕。從設計模式上來說可以視作裝飾者模式

用法

let newStore = applyMiddleware(mid1, mid2, mid3, ...)(createStore)(reducer, initialState);

下面是applyMiddleware的源碼

import compose from './compose'export default function applyMiddleware(...middlewares) {  return createStore => (...args) => {    const store = createStore(...args)    let dispatch = () => {      throw new Error(        `Dispatching while constructing your middleware is not allowed. ` +          `Other middleware would not be applied to this dispatch.`      )    }    let chain = []    const middlewareAPI = {      getState: store.getState,      dispatch: (...args) => dispatch(...args)    }    chain = middlewares.map(middleware => middleware(middlewareAPI))    dispatch = compose(...chain)(store.dispatch)    return {      ...store,      dispatch    }  }}

下面是一個middleware樣本的代碼(來自官方文檔):

/** * 記錄所有被發起的 action 以及產生的新的 state。 */const logger = store => next => action => {  console.group(action.type)  console.info('dispatching', action)  let result = next(action)  console.log('next state', store.getState())  console.groupEnd(action.type)  return result}

我在看原始碼的時候主要提了四個問題:

1. 為什麼要傳入createStore
2. …args是什麼
3. compose的作用
4. middleware這麼多函數嵌套,每次傳入的參數是什麼以及作用

通過對以上問題的一一解答,加深了我對applyMiddleware源碼的理解。

為什麼要傳入createStore

通過以上這一句可以看出就是為了單純的建立store。只不過是通過middleware把它包裹在洋蔥的最深處。

…args是什麼

我把args展開,可以看出它就是Redux原生createStore方法的標準參數,這也從另一方面回答了第一個問題。

compose的作用

compose本身並非Redux特有的概念,而是函數式編程的一種思想。關於函數式編程的入門可以參考阮大神的舊作:函數式編程初探。我在這裡只談compose.

compose顧英文名思中國義即組合函數,將多個函數組合起來串聯執行,一個函數的輸出作為另一個函數的輸入,一旦第一個函數開始執行,過程就會像多米諾骨牌。

註:當初對compose有個小小的疑惑。為什麼compose中的函數參數是從右向左執行。
根據以下代碼可以看出。看出啥我也很難描述,懂的自然懂(參數排列符合讀取順序,而執行順序恰好相反)。

let result = compose(f1, f2, f3, f4)(value);// 等價於let result = f1(f2(f3(f4(value))));

middleware這麼多函數嵌套,每次傳入的參數是什麼以及作用

將applyMiddleware和middleware樣本做一個對比:

從上圖可以得出:
1. middlewareAPI其實就是一個暴漏了getState和dispach方法的簡約版store。
2.在middleware中,next(action)是來派發action的,合理懷疑就是store的dispatch,通過對比applyMiddleware中的代碼可以證實這一點。

為了印象深刻,我的路徑是先假設有一個middleware,再擴充至多個:

一個middlwware

//源碼中dispatch = compose(...chain)(store.dispatch) //等價於fnMiddle = fn(middlewareAPI);dispatch = fnMiddle(store.dispatch)//等價於dispatch = fn(middlewareAPI)(store.dispatch)
//常用的store.dispatch(action) //等價於fn(middlewareAPI)(store.dispatch)(action)//正好對應middleware的三個參數store, next, action

多個middlwware: fn1, fn2

//源碼中dispatch = compose(...chain)(store.dispatch) //等價於dispatch= fn1Middle(fn2Middle(store.dispatch))// fn1Middle = fn1(middlewareAPI) etc..

讀到這裡就有點意思了, 首先執行的是fn2Middle(store.dispatch),返回結果是如下的一個函數。緊接著fn1Middle( (action) => {} ),這個action函數相當於佔據了fn1中next參數的位置。在收到action參數之前呢函數並不會執行。那麼在收到acton參數之後呢,繼續用語言描述實在是太難了,我試著畫了一個圖,希望在回看時不要再燒很多腦。

// fn2Middle(store.dispatch)執行後(action) => {    ....    next(action)    ....}

再補充一點:

let newStore = applyMiddleware(mid1, mid2, mid3, ...)(createStore)(reducer, initialState);

插入中介軟體後形成了newStore。此時newStore.dispatch方法不再是原生的redux store.dispatch了,而是依次放入了中介軟體的邏輯,原生store.dispatch放入了洋蔥模型的最裡面。

dispatch= fn1Middle(fn2Middle(store.dispatch))

上述實現的關鍵在於applyMiddleware源碼最後的return值,這個ES6風格的寫法的結果就是後加入的dispatch方法覆蓋了原生store.dispatch

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

重新梳理一遍清楚多了,這麼燒腦目測過半個月准忘,記錄的意義就像redux-devtool中的時光旅行一樣,在紛紛擾擾的操作之後還可以找回這片初心。

祝自己以及所有人春節快樂。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.