探一探前端的狀態管理

用一個通俗易懂的概念來解釋狀態管理:全局變量。

但是在項目的開發中,我們應該都記得前輩的叮囑:儘量不要或少用全局變量,因為那是不可控的操作,任意的操作都可能導致數據的改變,造成全局汙染,無法追蹤數據變化過程,所以開發中我們小菜鳥也大都避免去過多的操作全局變量。

但當項目複雜度比較高時,也許多個組件、多個頁面之間需要實現一種數據或狀態的共享,這時候,我們就可以將這些狀態統一的進行管理,既可以實現共享狀態,又能追蹤到數據的變化,可能這也是狀態管理誕生的一種原因吧。

當然,跟全局變量一樣,各個文章裡面也是叮而囑之,如果沒有哦必要,儘量不要使用狀態管理。

狀態管理應用包含三部分:

  • state,驅動應用的數據源;
  • view,以聲明方式將 state 映射到視圖;
  • actions,響應在 view 上的用戶輸入導致的狀態變化。

Flux 架構思想

關於如何在UI試圖中合理地修改state,Facebook提出了flux思想。

Flux 是一種架構思想,專門解決軟件的結構問題。後來的rudux和vux也都是由Flux的思想演化出來的。

Flux將一個應用分成四個部分: View(視圖)、Action(動作)、Dispatcher(派發)、Store(數據)

探一探前端的狀態管理

整體流程:

這是一個單向流動的過程,

用戶訪問view -> 觸發事件action -> Dispatcher知道後告知store要更新 -> store更新,觸發視圖View更新頁面

特點:

  • 單向數據流
  • Store 可以有多個
  • Store 不僅存放數據,還封裝了處理數據的方法(即可以直接修改數據狀態)

Redux

redux的核心概念:

Redux store 是單一數據源,且store是 不可變 的, Redux 沒有 dispatcher 的概念

探一探前端的狀態管理

特點:

  • 單向數據流
  • 單一數據源,只有一個store
  • state只讀
  • 沒有 Dispatcher ,而是在 Store 中集成了 dispatch 方法, store.dispatch() 是 View 發出 Action 的唯一途徑

Store 中提供了幾個管理 state 的 API:

  • store.getState() :獲取當前 state
  • store.dispatch(action) :觸發 state 改變(唯一途徑)
  • store.subscribe(listener) :設置 state 變化的監聽函數(若把視圖更新函數作為 listener 傳入,則可觸發視圖自動渲染)

整體流程:

Action Creator -> 觸發action -> store.dispatch(action) -> reducer(state, action) -> 更新state

MobX

MobX 是通過透明的函數響應式編程使得狀態管理變得簡單和可擴展,是一個用法簡單優雅、同時具有可擴展性的狀態管理庫。

探一探前端的狀態管理

和 Redux 對單向數據流的嚴格規範不同,Mobx 只專注於從 store 到 view 的過程。在 Redux 中,數據的變更需要監聽,而 Mobx 的數據依賴是基於運行時的,這點和 Vuex 更為接近。

如果大家使用過 Vue 的話相信對其雙向綁定 MVVM 的思想並不陌生,React + Mobx 相當於是 Vue 全局作用域下的雙向綁定,而 Vue 的狀態管理框架 Vuex 卻是借鑑了 Flux 架構,連尤大都說,似乎有點你中有我,我中有你的關係。

特點:

  • 往往是多個 Store
  • 數據流流動不自然,只有用到的數據才會引發綁定, 局部精確更新
  • 一般適合應用於中小型項目中

Vuex

仍然放一張vuex的示意圖:

探一探前端的狀態管理

vuex的運作原理跟redux稍有不同。因為Vue雖然是單向數據流,但Vue 基於 ES5 中的 getter/setter 來實現視圖和數據的

雙向綁定 ,因此 Vuex 中 state 的變更可以通過 setter 通知到視圖中對應的指令來實現視圖更新。且state不是通過actions來修改的,而是通過mutations。

特點:

  • 單向數據流, 通過 store.dispatch() 調用 Action ,在 Action 執行完異步操作之後通過 store.commit() 調用 Mutation 更新 State
  • 單一數據源
  • actions 可以是 異步操作,故可在action中調用後臺接口獲取新的數據;
  • mutations 只能是 同步操作;
  • mutations 和 actions 都可直接更改 state,但是當 action 含有異步操作時,會使得數據變化混亂,難以跟蹤,使得調試困難;基於以上原因,Vuex 規定只能是 mutations 來改變 state。

整體流程:

用戶訪問view -> 觸發事件action ->action中觸發對應的mutation -> 在mutation函數中改變state -> 通過 getter/setter 實現的雙向綁定的機制,視圖View會自動更新頁面

使用一句話總結: commit mutation,dispatch action

幾種方案的異同

從上面幾種熱門的狀態管理實現方式來看,可以發現,這幾種數據流模型幾乎都是從 action 到 view 之間的一種數據流動,總的過程可以大致簡化為:

action -> 更新 state(視圖層)

1、 數據狀態中心: state和store

可以發現,在redux和flux中,數據狀態用store表示,而在vuex和mobx中,這個store被state替代。

和 Redux 中使用不可變數據來表示 state 不同, Vuex 中沒有 reducer 來生成全新的 state 來替換舊的 state,Vuex 中的 state 是可以被修改的 。

Vuex 中的 state 是可修改的,而修改 state 的方式不是通過 actions,而是通過 mutations。更改 Vuex 的 store 中的狀態的唯一方法是提交 mutation。

2、 行為action

我們想要更新視圖內容,第一步就是要有觸發的動作,action就是向store發起更新請求的最小單元。但action並非是一個動詞,它是一個行為的描述,本質上是 一個純聲明式的數據結構,僅提供對事件的描述,不提供事件的具體邏輯

在rudux中,生成action有兩種方式:

  • 聲明對象:const action = { type: 'ADD_TODO', text: '生成一個action' } dispatch(action) 複製代碼
  • 函數生成:function addTodo(text) { return { type: ADD_TODO, text } } dispatch(addTodo()) 複製代碼

3、dispatch

在各類應用狀態管理的模型中,通常都會有一個 dispatch 方法,它就聲明在 Store 上,負責調用各個 Action ,然後由 Store 上對應的分發機制進行處理。

4、Reducer / Mutation

現在到了我們的第三步,更新狀態。flux和mobx對狀態更新的處理是直接更改,而redux和vuex中間還會多出一個reducer和mutation來處理更新的工作。

至於reducer和mutation,分別是針對redux和vuex的不同工作機制。

先看reducer, Actions 只能描述發生了什麼,並不能描述狀態發生了什麼變化, Reducers 指定 state tree 將要發生什麼,它主要的工作內容是:接受一個action,然後通過switch匹配action的type,作出相應的處理後,返回一個 新的對象 。為什麼是新的對象呢?因為redux最核心的一個概念,store是 不可變 的, Redux 是一個實踐 函數式編程(FP) 理念的庫 。下面是一個最簡單的reducer:

<code>const items = (state = [], action) => {  switch (action.type) {    case "ADD_ITEM":      return [...state, { text: action.text }]    default:      return state  }}/<code>

再來看mutation,一個 mutation 是由一個 type 和與其對應的 handler 構成的,type 是一個字符串類型用以作為 key 去識別具體的某個 mutation,handler 則是對 state 實際進行變更的函數。

<code>// storeconst store = {  books: []}// mutationsconst mutations = {  [ADD_BOOKS](state, book) {    state.books.push(book)  }}/<code>


分享到:


相關文章: