redux 源碼解讀-applyMiddleware

hello,大家好,之前我們大概分析了一下createStore中的代碼,但應該還是有很多迷迷糊糊的,感覺不知所以。畢竟這個redux中有很多的牽扯,所以當你把整個redux源碼都看了一遍,就會大概的瞭解redux的原理
下面我們來分析一下applyMiddleware 
export default function applyMiddleware(...middlewares) { // 傳入中間件的數組
  return (createStore) => (reducer, preloadedState, enhancer) => { 返回一個函數
    const store = createStore(reducer, preloadedState, enhancer) // 創建store
    let dispatch = store.dispatch // 獲得store的dispatch
    let chain = [] // 聲明空數組 用來存儲action增強函數

    const middlewareAPI = { // 
      getState: store.getState, // 通過store獲取state
      dispatch: (action) => dispatch(action) // dispatch方法
    }
    // 傳進來的middlewares 進行遍歷 返回新數組
    chain = middlewares.map(middleware => middleware(middlewareAPI)) 
    dispatch = compose(...chain)(store.dispatch) // 使用compose進行柯里化
    // 在使用了middleWare的情況下,這裏就是我們createStore的返回值。
    return {
      ...store,
      dispatch
    }
  }

大體代碼是做什麼的已經用註釋講解了,但是可能大家還是有一點朦朦的。

例如爲什麼applyMiddleware這個函數 又返回了一個多重箭頭函數, 爲什麼disaptch這樣就能增強action

這個就會牽扯到我們上一篇講的createStore

const enhancer = compose(
  applyMiddleware(...middlewares)
);
const store = createStore(
  combineReducers({ ...reducers }),
  initialState,
  enhancer
);

這裏的enhancer 等價於 applyMiddleware(...middlewares)

在createStore中傳入enhancer 和 applyMiddleware(...middlewares)效果是一樣的。

所以相當於applyMiddleware(...middlewares)這個就是createStore傳入的 enhancer,在createStore中enhancer要求是一個函數,

現在我們看看applyMiddleware(...middlewares)的返回結果,這裏返回了一個函數。

if (typeof enhancer !== 'undefined') {
  if (typeof enhancer !== 'function') {
    throw new Error('Expected the enhancer to be a function.')
  }

  return enhancer(createStore)(reducer, preloadedState)
}

從上面的代碼我們可以看到我們首先將createStore傳入 applyMiddleware(...middlewares)返回的函數中,然後在將state和reducer傳入到enhancer(createStore)返回的函數中。

這樣我們就可以知道在我們傳入中間件的情況下

const store = createStore(reducer, preloadedState, enhancer) // 創建store

這一行代碼纔是創建store執行者。

然後我們通過創建的store或者state 和 dispatch 然後創建了一個middlewareAPI 並將其傳入middleware中,

這是爲了讓中間件可以直接和store來進行溝通。

接下來的兩行代碼可以說是applyMiddleware中最核心的代碼了

chain = middlewares.map(middleware => middleware(middlewareAPI))

首先通過map將其遍歷 執行中間件。

我們用一下redux-thunk的源碼來進行對應的講解

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;

這裏獲得的thunk 是createThunkMiddleware的執行結果  thunk相當於 

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

  return next(action);
};

當我們只有middleWare中只有一個thunk的時候

這時候我們applyMiddleware中的 middlerwares  = [ thunk ] 

chain = middlewares.map(middleware => middleware(middlewareAPI))

就相當於 chain  = middlewares.map(thunk = thunk(middlewareAPI));

執行結果: chain = [ thunk() ]

關於thunk多重箭頭函數中的next 和 action 是從哪裏來的呢? 

具體是由於下面這一句代碼的執行,所引起的

dispatch = compose(...chain)(store.dispatch)

export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

以上是compose的源碼

我們看一下里面最核心的就是最後一句代碼

return funcs.reduce((a, b) => (...args) => a(b(...args)))

這裏使用了reduce操作,我們假設傳進去的數組裏面有3箇中間件A,B,C,然這執行後返回的結果爲(..args)=>A(B(C(...args))) 這樣一個函數。

然後我們在看看dispatch = compose(...chain)(store.dispatch) 這一行,我們將store.dispatch 傳遞了進去,所以我們 上面的

(..args)=>A(B(C(...args)))    =>  (store.dispatch) => A(B(C(store.dispatch)));

我們這時候可以看看thunk中間件代碼,然後結合上面推出來的表達式,我們能看到如果是最後一箇中間件,那麼他的next是store.dispatch,如果不是最後一個,那麼他的next就是 後一個函數的執行結構。

我們可以推斷出來 (store.dispatch) => A(B(C(store.dispatch))) 這一行的返回結果是

以thunk代碼爲例: 我們執行後會返回這樣一個箭頭函數。 我們下文將這個箭頭函數簡稱爲Action函數

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

  return next(action);
};

所以不管有多少箇中間件我 compose(...chain)(store.dispatch) 這裏返回的一定是Action函數,

然後把這個賦值給dispatch,這個dispatch就是我們正常使用的dispatch函數。

我們在使用redux的時候會調用dispatch這個方法 eg: disptach(action) ; 

在我們調用的時候我們將action傳遞進去,然後傳遞到 compose(...chain)(store.dispatch) 返回的函數,執行函數體,然後我們最後返回next(action) 這裏的next指向下一個中間件的 Action函數,直到最後面指向store.dispatch;

現在基本解釋完了,我們接下來看源碼 最後返回了一個對象裏面返回了創建的store對象,和增強後的dispatch方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章