對於Redux原始碼的一些理解

ZHONGJIAFENG7發表於2019-03-27

Redux很早前看過原始碼,不得不說Redux是一個有用的架構,接觸過Redux對於之後理解React-Redux有很大的幫助。最近學習了一段時間後打算重新學習下,一方面為了總結和歸納,另一方面分享給大家,如果有什麼不足之處希望各位大牛的糾錯和指正。本文主要包含如下幾個分析點:

  • applyMiddleware
  • compose
  • thunk中介軟體
// 該檔案的核心函式部分共傳入了三個引數
// reducer, preloadedState, enhancer
function createStore(reducer, preloadedState, enhancer){
    ...
    if (typeof enhancer !== 'undefined') {
        return enhancer(createStore)(reducer, preloadedState)
    }
    ...
}
複製程式碼

createStore方法的作用是用來建立一個倉庫來存放state,subscribe,reducer以及dispatch,state用來存放資料的地方,通過store.getState()來獲取;subscribe用來加入監聽函式,當頁面資料改變的時候會進行觸發,dispatch用來派發action,根據不同的型別匹配不同的reducer,繼而進行state資料的更新,具體可以參考阮一峰老師的部落格 Redux 入門教程

其中enhancer函式的作用顧名思義就是用來擴充套件加強的,這個函式的存在使得可以隨意的加入早就想要的中介軟體,從而更加方便快捷,這裡的加強函式主要是applyMiddleware

applyMiddleware

這裡使用連續箭頭函式,簡單明瞭更重要的一點是形成了閉包,閉包的存在使得內部的變數可以很自由的訪問外部被多個return巢狀的變數, 從而使得每個中介軟體都獲得middlewareAPI引數,引用外部的dispatch變數,這樣避免瞭如果存在一箇中介軟體修改了dispatch導致後面一系列的問題。
export default function applyMiddleware(...middlewares) {
  return (createStore) => (reducer, preloadedState, enhancer) => {
    // 建立倉庫
    const store = createStore(reducer, preloadedState, enhancer)
    let dispatch = store.dispatch
    let chain = []

    const middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action) // 通過閉包引用外部的dispatch變數
    }
    // 將middlewareAPI傳入中介軟體,使得每個中介軟體都獲得{getState,dispatch}引數,而由於閉包的原因,我們就可以在中介軟體當中獲取最新的store以及引用外部更新的dispatch變數。
    // @return 返回包含(next) => (action) => {....}的陣列
    chain = middlewares.map(middleware => middleware(middlewareAPI))
    // 建立增強功能的dispatch
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}
複製程式碼

thunk中介軟體

以thunk中介軟體為例,當需要派發的action是非同步函式而不是物件的時候需要這個中介軟體,則此時chain即為[(next)=>(action)=>{...}, ....], 如果action為函式則直接執行該函式,並且傳入dispatchgetSate這兩個引數,否則執行next方法直到最後next函式為dispatch進行action派發。
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;
複製程式碼

compose

理解compose方法首先需要了解reduce,reduce()方法接收一個函式作為累加器,陣列中的每個值(從左到右)開始縮減,最終計算為一個值,所以最終的形式是a(b(c(d...(...args))))

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)))
}
複製程式碼

之前chain返回包含(next) => {...}的thunk,logger中介軟體陣列,從而得到a_middleware方法裡的next方法就是b_middleware,b_middleware方法裡的next方法是c_middleware,以此類推,根據applyMiddleware.jscompose(...chain)(store.dispatch)以及chain返回值可以知道, 最後的next引數是dispatch,大概步驟如下:

  • 第一步:當我們把store.dispatch傳入c_middleware時,這時就把store.dispatch傳給了 c_middleware的next變數,返回一個(action)=>{next=store.dispatch}[1]函式;
  • 第二步:接下來就是把[1]傳給b_middleware的中介軟體的next,這時候中介軟體b_middleware內部的next就變成(action)=>{[1]}[2], 當執行b_middleware的時候,會進入c_middleware;
  • 第三步:接下來就是把[2]的結果傳遞給a_middleware, (action)=>(action)=>{[1]}傳遞給a-middleware的next變數,當執行a_middleware的時候會進入b_middleware。

從下面大概例子可以看出disatch: (...args) => dispatch(...args)的好處,dispatch隨時隨地隨著dispatch = compose(...chain)(store.dispatch)更新而更新,從而當傳入為方法的時候,也不會忽略其他中介軟體,再次dispatch的時候會流過所有thunk之後的中介軟體。

a_middleware(b_middleware){
    ...
    b_middleware = next = (action) => {
        dispatch = (action) => {
            dispatch = (action)=>{store.dispatch函式}
            (action) => {dispatch}
        }
        return (action) => {dispatch}
    }
    return next(action)
    ...
}
b_middleware(c_middleware){
    ...
    c_middleware = next = (action) => {
        dispatch = (action)=>{store.dispatch函式}
        return (action) => {dispatch}
    }
    return next(action)
    ...
}
c_middleware(store.dispatch){
    return (action)=>{store.dispatch函式}
}
複製程式碼

總結

1.首先將store.getStatestore.dispatch通過閉包的方式使得中介軟體可以訪問;

2.其次,通過compose函式操作,對next進行賦值,使得中介軟體按順序依次執行;

3.最後,返回一個dispatch函式,可以通過傳入action引數,使得中介軟體按順序依次執行,如果action為函式則直接執行該函式,並且傳入dispatch和getSate引數。

總之使用redux進行狀態管理極大地提高了工作效率,讓資料更好地被管理。

相關文章