剖析redux中間件applyMiddleware

Middleware是什麼

Middleware 只是包裝了 store 的 dispatch 方法。技術上講,任何 middleware 能作的事情,均可能經過手動包裝 dispatch 調用來實現,可是放在同一個地方統一管理會使整個項目的擴展變的容易得多。

從新包裝 dispatch 方法

爲何要從新包裝 dispatch

中間件的做用可讓咱們決定何時調用dispatch,可能在promise函數執行完或在action裏面執行回調函數後,這就須要對舊的dispatch函數進行從新包裝,讓它可以先執行中間件函數裏面的方法,並把真正的dispatch傳遞給中間件函數:redux

let storeDispatch = stroe.dispatch;// 取出store的dispatch方法保存

// 重寫dispatch方法
stroe.dispatch = function (action) {
  console.log('before dispatch')
  stroe.dispatch(action) // 在適當的時候調用真實的dispatch方法
  console.log('after dispatch')
}

只有一箇中間件

logger中間件

咱們能夠寫一個logger中間件來進一步瞭解:數組

function logger(store){
  return (dispatch)=>{
    return (action)=>{
        console.log("before logger");
        store.dispatch(action);
        console.log('after logger')
    }
  }
}

中間件是一個柯里化組合的函數,每一個層級都包裝有對應的函數參數供咱們調用,真正執行的dispatch方法其實在最後一個返回的函數裏面。promise

applyMiddleware

扯了這麼多還沒看看真正的applyMiddleware函數長什麼樣子:app

function applyMiddleware(middlewares) { // @params 中間件數組
  return function (createStore) { // @params 建立store函數
    return function (reudcer) { // @params reducer
        return store; // @return 返回 createStore(reducer)
    }
  }
}

applyMiddleware也是一個柯里化組合的函數,不過最終返回的是一個store,照上面說的,redux中間件處理的是dispatch方法,這裏也把storedispatch方法從新包裝一下:函數

function applyMiddleware(middleware) {
  return function (createStore) {
    return function (reudcer) {
      let store = createStore(reudcer);
      let dispatch = ()=>{throw new Error("dispatch 還不能用,還沒改形成next方法")};
      dispatch = middleware(store)(store.dispatch); // 改造包裝後的dispatch; 對應logger(store)(dispatch)
      return {
        ...store,
        dispatch
      }
    }
  }
}

// 根據applyMiddleware須要返回的函數傳入對應的值
applyMiddleware(logger)(createStore)(reducer);

這樣每次在組件中調用storedispatch方法時,其實調用的是通過中間件logger包裝後的dispatchspa

function dispatch(action){
  console.log('before logger')
  store.dispatch(action) // 這裏纔是真實調用store.dispatch;
  console.log('after logger')
}

多箇中間件

多箇中間件的狀況比較複雜,須要保證每一箇中間件方法都能執行,而且可以像洋蔥模型同樣,dispatch方法可以在最裏面執行:code

function applyMiddleware(...middlewares) {
  return function (createStore) {
    return function (reudcer) {
      const store = createStore(reudcer)
      const middlewareAPI = {
        getState: store.getState,
        dispatch: (...args) => dispatch(...args),
      }
      
      // 將中間件函數先遍歷,執行裏面的方法並返回,獲得一個dispatch方法的中間件數組
      const chain = middlewares.map((middleware) => middleware(middlewareAPI))
      
      // compose函數會將包含了dispatch方法的中間件數組組合成一個嵌套的包裝函數返回
      const dispatch = compose(...chain)(store.dispatch)
      
      return {
        ...store,
        dispatch,
      }
    }
  }
}

這裏compose方法把中間件裏面的dispatch方法給包裝到了一塊兒,讓中間件可以一層層往裏執行:中間件

//compose
function compose(...funcs) {
  if (funcs.length === 0) {
    return (args) => args
  }
  if (funcs.length === 1) {
    return funcs[0]
  }
  return funcs.reduce((a, next) => (...args) => a(next(...args)))
}

這裏的reduce很差理解,把它拆成函數,可能會好理解一點:get

function compose(...middlewarw) {
  return function(storeDispatch) {

    function dispatch(index, action) {
      let fn = middlewarw[index]
      
      // 回調裏面的 next 方法,執行的是下一個中間件函數
      let next = () => dispatch(index + 1, action)
      // 若是還有下一個中間件就繼續執行,並把action傳進去,不然執行store.dispatch方法
      fn ? fn(next)(action) : storeDispatch(action)
    }

    return (action) => dispatch(0, action)
  }
};

經常使用中間件

另外經常使用到的中間件,這裏也寫下它的源碼實現:回調函數

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;

redux-promise

function isPromise(obj) {
    return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
}
export default function promiseMiddleware({ dispatch }) {
    return next => action => {
        return isPromise(action.payload)
            ? action.payload
                .then(result => dispatch({ ...action, payload: result }))
                .catch(error => {
                    dispatch({ ...action, payload: error, error: true });
                    return Promise.reject(error);
                })
            : next(action);
    };
}
相關文章
相關標籤/搜索