redux-applyMiddleware實現理解+自定義中間件

前言:html

  終於好好理解了middleware。。。。es6

1.redux middleware提供的是位於 action 被髮起以後,到達 reducer 以前的擴展點。redux

  redux經過store.dispatch(atcion),發起一個action給store,store接收後把當前state與action一塊兒傳給reducer,因此middleware作文章的地方就是dispatch,在原生的dispatch執行以前,先進行一些列的操做。api

 

實現middleware的方法:下面以logger中間件爲例app

1.手動記錄(不可能會使用這種的吧)異步

let action = addTodo('Use Redux')

console.log('dispatching', action)
store.dispatch(action)
console.log('next state', store.getState())

 

2.封裝dispatch函數

  要發起action的時候,不用 dispatch 而是用 disptchAddLog()。(把這個方法放在store裏面、好像還不錯的樣子)spa

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

 

3.猴子補丁code

  經過重寫store.dispatch(), (重寫以後,所使用的dispatch(), 已經不是原生的dispatch了,使用多箇中間件,就是不斷改寫前一次生成的dispatch),htm

  (注意:原生的dispatch,已經不可能找到,也就是不能單獨使用原始dispatch了)

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

 

 

4.隱藏猴子補丁,

  乍一看好像和猴子補丁沒什麼卻別,但其實它把賦值的給store.dispatch的邏輯放到了中間件函數的外面,須要外面提過一個applyMiddleware輔助函數來完成插值,除此以外,真的沒什麼區別。(原理仍是猴子補丁)

function logger(store) {
  let next = store.dispatch

  // 咱們以前的作法:
  // store.dispatch = function dispatchAndLog(action) {

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

 

  多箇中間件 的實現方式:

function applyMiddlewareByMonkeypatching(store, middlewares) {
  middlewares = middlewares.slice()
  middlewares.reverse()

  // 在每個 middleware 中變換 dispatch 方法。
  middlewares.forEach(middleware =>
    store.dispatch = middleware(store)
  )
}

 

5.移除猴子補丁

  相對隱藏猴子不一樣,把middleware函數裏面 let next = store.dispatch ,放到函數外面 dispatch = middleware(store)(dispatch) 

function logger(store) {
  return function wrapDispatchToAddLogging(next) {
    return function dispatchAndLog(action) {
      console.log('dispatching', action)
      let result = next(action)
      console.log('next state', store.getState())
      return result
    }
  }
}

 

  看一下多箇中間件實現方式你就知道了,

  是否發現:在 return 的 assign 以前, store.dispatch 都是原生的,並無被改變。因此中間件裏面是否可使用store.dispatch調用原生的,(若是有須要的話),然而筆者試着調用了一下,並不行,會不斷的觸發,就是已是該改變以後的了。

// 警告:這只是一種「單純」的實現方式!
// 這 *並非* Redux 的 API.

function applyMiddleware(store, middlewares) {
  middlewares = middlewares.slice()
  middlewares.reverse()

  let dispatch = store.dispatch
  middlewares.forEach(middleware =>
    dispatch = middleware(store)(dispatch)
  )

  return Object.assign({}, store, { dispatch })
}

 

  logger中間件換成es6的箭頭函數更好看

const logger = store => next => action => {
  console.log('dispatching', action)
  let result = next(action)
  console.log('next state', store.getState())
  return result
}

 

 

6.redux中是現實方法

這與 Redux 中 applyMiddleware() 的實現已經很接近了,可是有三個重要的不一樣之處

  • 它只暴露一個 store API 的子集給 middleware:dispatch(action) 和 getState()

  • 它用了一個很是巧妙的方式來保證你的 middleware 調用的是 store.dispatch(action) 而不是 next(action),從而使這個 action 會在包括當前 middleware 在內的整個 middleware 鏈中被正確的傳遞。這對異步的 middleware 很是有用。

  • 爲了保證你只能應用 middleware 一次,它做用在 createStore() 上而不是 store 自己。所以它的簽名不是 (store, middlewares=> store, 而是 (...middlewares=(createStore=> createStore

 

總結:

  redux提供了applyMiddleware(), 若是咱們自定義中間件,也就變的很簡單了

function myMiddleware = (store) => (next) => (action) => {
    console.log('進入了我本身定義的中間件');
    let result = next(action);    // 執行下一步
    // let result = store.dispatch(action); //不調用next,直接使用store.dispatch調用原生。而後是不行的,會陷入dispatch死循環。
    return result;
}

  applyMiddleware會幫咱們執行前面兩層函數。myMiddleware(store)(dispatch)

redux-thunk的實現也是很牛掰

function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }

    return next(action);
  };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;
相關文章
相關標籤/搜索