簡析redux技術棧(一):redux中間件

本文地址git

通常使用了中間件的 redux 初始化是下面這樣的github

function configureStore(initialState) {
  return {
    ...createStore(
      reducer,
      initialState,
      applyMiddleware(middleware1, middleware2, middleware3)
    )
  };
}

const store = configureStore({});
複製代碼

redux 中間件是一個函數,形式redux

const middleware1 = store => next => action => {
  console.log("before", 1);
  next(action);
  console.log("after", 1);
};

const middleware2 = store => next => action => {
  console.log("before", 2);
  next(action);
  console.log("after", 2);
};

const middleware3 = store => next => action => {
  console.log("before", 3);
  next(action);
  console.log("after", 3);
};
複製代碼

須要作到bash

  • 保證每個中間件內的 store 引用都是最新的,而且是同一個app

  • 鏈接起來。執行一次 dispatch,會依次執行每個中間件異步

代碼解釋函數

function applyMiddleWare(...middlerWares) {
  return createStore => (...args) => {
    const store = createStore(...args);
    let dispatch = () => {};
    const middlerWareAPI = {
      dispatch: action => dispatch(action),
      getState: () => store.getState
    };
    const chain = middlerWares.map(middleWare => chain(middlerWareAPI));

    dispatch = chain.reduce((f, g) => (...args) => f(g(...args)))(
      store.dispatch
    );

    return {
      ...store,
      dispatch
    };
  };
}
複製代碼

從上面能夠看到,一箇中間件函數要最後完成,須要再執行前通過兩次的初始化(分別傳入 store 和 next 方法),而後到最後的調用測試

  • 第一次調用。
const chain = middlerWares.map(middleWare => chain(middlerWareAPI));
複製代碼

store兩個方法傳遞給中間件,全部中間件內都是同一份storefetch

  • 第二步調用也是最關鍵的地方,就是將全部的中間件串聯起來,dispatch一次就執行全部的中間件
// 這裏其實就是compoose的實現
dispatch = chain.reduce((f, g) => (...args) => f(g(...args)))(store.dispatch);
// 等價於相似 下面的形式
dispatch = middleWare1(middleWare2(middleWare3(dispatch)));
複製代碼

咱們知道,通過第一步的初始化,對於middleWare1函數,可見next參數就是指向了middleWare2(dispatch)。只有咱們的next調用了纔會執行後面的中間件。而到了最後一箇中間件middleWare3,它的next參數就是 redux 對應的dispatch函數了。從而最終把咱們的action派發到store中去。而後調用棧依次返回。就像一個剝洋蔥同樣的東西。ui

因此若是咱們把開頭的 3 箇中間件組合起來運行的話,

輸出是

before 1
before 2
before 3
通過reducer // reducer中的log測試
after 3
after 2
after 1
複製代碼

通過上面的代碼,咱們還能夠發現applyMiddleWare其實也是一個高階函數。applyMiddleware(middleware1, middleware2, middleware3)執行後,是一個接收createStore參數的函數。在createStore裏面又是如何操做的。源碼:

function createStore(reducer, preloadedState, enhancer) {
// .....enhancer就是上面提到的 applyMiddleware(middleware1, middleware2, middleware3)返回結果
  if (typeof enhancer !== "undefined") {
    return enhancer(createStore)(reducer, preloadedState);
  }
// ....
複製代碼

其實就是把原始的createStore再傳進入,進行建立store。返回一個更牛逼的store,這個store其實只是重寫了dispatch方法。

經過上面一堆分析,有幾個結論了:

一、 中間件內部必須調用next方法。才能調用下一個中間件而且到達 action

二、中間件內部若是調用了dispatch(重寫後的)。不加處理就會死循環,至關於這個洋蔥剝到中間又開始剝了。

那麼問題來了,dispatch何時用呢?

這就提到一個大名鼎鼎的庫redux-thunk。它的源碼裏面就是使用了這個dispatch

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-thunk相信都很熟悉,通常用於處理編寫異步邏輯下。它的源碼更是牛逼。只有十幾行。從上面能夠看出它也是一箇中間件,它的邏輯就是容許你dispatch一個函數,當你 dispatch 一個函數的時候,就直接執行它,並傳入了dispatch(注意這個 是 dispatch 不是 前面中間件提到的 next)和getState方法

const fetchApi = (...args) => (dispatch, getState) => {
  setTimeout(() => {
    dispatch({ type: "xx", payload: "good" });
  }, 7000);
};

dispatch(fetchApi("xxx"));
複製代碼

經過前面的分析,傳進thunk內的dispatch參數就是通過包裝的dispatch,因此當咱們去分發一個同步的普通action的時候,它又能通過咱們的其他普通中間件邏輯的處理了

reduxapplyMiddleWare函數和redux-thunk。代碼很簡短,卻隱藏着大智慧

相關文章
相關標籤/搜索