深入淺出理解Redux

weixin_34357887發表於2018-05-11

Redux是什麼

Redux 是 JavaScript 狀態容器,提供可預測化的狀態管理。

它認為:Web應用是一個狀態機,檢視與狀態一一對應。從架構層面來說,通常希望UI跟資料、邏輯分離,直觀體現就是:UI = render(state)

為什麼要用Redux

現在的Web應用涉及大量資料互動、非同步操作等,無疑都在增加前端的複雜性,需要維護的state也越來越多。而Redux就是試圖讓每個state變化都是可預測,將應用中所有的action與state統一管理。

Redux的三原則

  • 單一資料來源 整個應用state應該只儲存在唯一一個的Store中。
  • 保持狀態只是只讀 不能直接修改state,唯一能改變Store的state方法就是通過觸發一個action物件完成。
  • 資料改變通過純函式完成 action改變state需要通過reducers。

Redux工作流程

在講Redux的工作流程之前,需瞭解幾個Redux相關的核心概念:

  • Action:Action可以看成是應用發出的通知,表示State應該要發生變化了,Action的觸發可能是使用者對View層的操作也可能是伺服器的響應。
  • Action Creator:如果有很多種Action,而每種Action都手寫的話顯得麻煩,所以用定義的Action Creator函式來生成Action。
  • Dispatch:Action發出的唯一方法。
  • Store:整個應用唯一儲存資料的地方。
  • State:對Store中儲存資料生成某個時點資料快照,該資料集合叫做State。
  • Reducer:Action只是描述了State應要發生變化,而Reducer做的是如何改變State。

具體工作流程: 使用者通過View(或伺服器響應)觸發Action,Dispatch方法將Action Creator函式生成的Action派發到Store,Store自動呼叫Reducer,並向它傳入當前State和Action,Reducer返回新的State,State一旦有變化,Store就會通過監聽函式來更新View。 借用一張圖來描述這一過程:

嚴格的單向資料流是 Redux 架構的設計核心。

幾個Redux核心概念例項

以從伺服器響應文章內容為例 Action

const LOAD_ARTICLES_DETAIL = 'LOAD_ARTICLES_DETAIL'
const LOAD_ARTICLES_DETAIL_SUCCESS = 'LOAD_ARTICLES_DETAIL_SUCCESS'
const LOAD_ARTICLES_DETAIL_ERROR = 'LOAD_ARTICLES_DETAIL_ERROR'
複製程式碼

Action Creator

export const loadArticlesDetail = () => ({
  type: LOAD_ARTICLES_DETAIL
})
export const loadArticlesDetailSuccess = result => ({
  type: LOAD_ARTICLES_DETAIL_SUCCESS,
  result
})
export const loadArticlesDetailFailure = error => ({
  type: LOAD_ARTICLES_DETAIL_ERROR,
  error
})
複製程式碼

Store

const store = createStore(reducers)
複製程式碼

Reducer (previousState, action) => (newState)

export default (state = initalState, action) => {
switch (action.type) {
case LOAD_ARTICLES_DETAIL: {
  return {
    ...state,
    loading: true,
    error: false
  }
}
case LOAD_ARTICLES_DETAIL_SUCCESS: {
  return {
    ...state,
    loading: false,
    error: false,
    articlesDetail: action.result
  }
}
case LOAD_ARTICLES_DETAIL_ERROR: {
  return {
    ...state,
    loading: false,
    error: true
  }
}
default:
  return state
}
}

複製程式碼

深入到Redux的原始碼

Redux主要原始碼整體結構:

  • 入口檔案 index.js
export {
  createStore,
  combineReducers,
  bindActionCreators,
  applyMiddleware,
  compose
}
複製程式碼

這是入口檔案匯出的方法,也是Redux支援的方法,這些方法的實現在主工作流程檔案和輔助函式檔案,接下來看主工作流程。

  • 主工作流程檔案 createStore.js createStore方法主要是生成Store,看看它做了哪些事兒:
    • getState方法返回了當前State
    • subscribe方法傳入函式到監聽佇列和返回取消訂閱函式
    • dispatch方法呼叫Reducer,按順序執行listener,返回Action 輔助原始碼檔案:
  • applyMiddleware.js:用於增強Store
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: (...args) => dispatch(...args)
    }
    chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)

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

從原始碼上看,最後是返回了一個Store和一個被更新過的dispatch方法,實現了對Store的增強。

  • bindActionCreators.js:
export default function bindActionCreators(actionCreators, dispatch) {
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }
}
複製程式碼

使用dispatch把action creator都包裝起來,這樣可以直接呼叫它們。

  • combineReducers.js:當應用比較大的時而拆分Reducer,但是傳入Store的Reducer必須是一個函式,所以這個方法的主要功能是用來合併多個Reducer。
  • compose.js
    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這個方法,傳入的一系列函式,執行的最終結果是把各個函式串聯起來。

總結:

  • Redux 是 JavaScript 狀態容器,提供可預測化的狀態管理。
  • 嚴格的單向資料流是 Redux 架構的設計核心。
  • UI = render(state)

相關文章