為管理復雜組件狀態困擾?試試 vue 簡單狀態管理 Store 模式【轉】


https://juejin.im/post/5cd50849f265da03a54c3877

在 vue 中,通信有幾種形式:

  • 父子組件 emit/on
  • vuex 中共享 state
  • 跨組件 EventBus

文檔中的提到的 Store 模式卻鮮有人去使用討論。筆者在研究 ElementUI的Table組件的代碼組織方式,以及在自己 ElementUI 表單編輯項目中實踐之后覺得其在復雜組件組織上非常有用,是一個被忽視的組件通信方法。

簡單狀態管理 store 模式

官方示例代碼:

var store = { debug: true, state: { message: 'Hello!' }, setMessageAction(newValue) { if (this.debug) console.log('setMessageAction triggered with', newValue) this.state.message = newValue }, clearMessageAction() { if (this.debug) console.log('clearMessageAction triggered') this.state.message = '' } } 復制代碼

官方介紹:所有 store 中 state 的改變,都放置在 store 自身的 action 中去管理。這種集中式狀態管理能夠被更容易地理解哪種類型的 mutation 將會發生,以及它們是如何被觸發。當錯誤出現時,我們現在也會有一個 log 記錄 bug 之前發生了什么。此外,每個實例/組件仍然可以擁有和管理自己的私有狀態

 

數據流圖

 

官方版的介紹過於簡陋,不妨我們更進一步,學習一下 ElementUI 的 Table 組件是如何用 Store 組織一個復雜組件的

為什么需要 Store 模式

ElementUI 的 Table 組件,功能很多。該組件由父組件 Table.vue 和眾多子組件 layout-observer,table-body,table-column,table-footer,table-header,table-layout 組成。看 ElementUI 文檔就覺得 Table 組件復雜。

如果把子組件的事件都 emit 到父組件處理,那么父組件得接收多少事件。並且子組件部分功能會影響父組件的布局。並且 Table 的部分數據大多數子組件都需要,你要一個一個通過 Porp 傳入嗎?自頂向下的數據流動開發困難。不如把這些共享的數據放在一個地方,我們自然很容易想到 Vuex,但是 ElementUI 庫引入 ElementUI 引入 Vuex,你覺得合適嗎,並且數據共享僅僅是在 Table 組件里面,並不是全局的數據,因此采用 Store 模式再好不過了。

ElementUI 模仿了 Vuex 的使用方式。有興趣的讀者可以看一下 Table 組件中table-store.js

模仿 Vuex 的一個好處就是我后期如果項目大了,可以十分平滑的引入 Vuex,並且如果你熟悉 Vuex,使用 Store 模式沒有任何認知成本

實踐

筆者用 Store 模式改造了我之前的ElementUI 的表單在線編輯器,之前的主頁面由表單元素資源區,表單屬性編輯區,表單元素拖拽區,表單元素屬性編輯區,JSON表單生成區,代碼生成區。然而整個頁面就維護表單對象,表單元素列表,表單元素屬性這幾個值,然而這些值在多個子組件里面都起了一定的作用,一開始沒有集中處理,導致數據會意外變化,不知道是那個組件引起的。后使用Store模式集中處理之后,代碼邏輯清楚很多

 

項目展示 這種方式其實就是把數據管理,數據更新的功能交給了 Store。如果你熟悉 Vuex 的話,應該很快能理解我在說什么

 

通過定義和隔離狀態管理中的各種概念並通過強制規則維持視圖和狀態間的獨立性,我們的代碼將會變得更結構化且易維護

 

數據流

 

聲明一個 Store 對象

const FormStore = function(form, initialState = {}) { // 將父組件的示例保存在Store里面 if (!form) { throw new Error('Form is required.') } this.form = form this.states = { ... } // initialState 里面的值必須是 this.states聲明過的,這樣所有狀態的變化應該都在store里面可以查找,並由store控制 for (let prop in initialState) { if (initialState.hasOwnProperty(prop) && this.states.hasOwnProperty(prop)) { this.states[prop] = initialState[prop] } } } 復制代碼

mutations

Vuex 中的 mutation 非常類似於事件:每個 mutation 都有一個字符串的   事件類型 (type)  和 一個   回調函數 (handler)。這個回調函數就是我們實際進行狀態更改的地方,並且它會接受 state 作為第一個參數

我們這里也模仿它,注意這里我們只放同步的代碼,異步代碼自己處理

FormStore.prototype.mutations = {
  setFormAttribute(states, formAttribute) {
    this.states = { ...states, formAttribute } }, setFormItems(states, formItems) { this.states = { ...states, formItems } }, setClickedIndex(states, clickedIndex) { this.states = { ...states, clickedIndex } }, setFormItemToHandle(states, formItemToHandle) { this.states = { ...states, formItemToHandle } }, setItemInFormItems(states, idx, formItem) { states.formItems.splice(idx, 1, formItem) }, setFromItems(states, formItems) { this.states = { ...states, formItems } } } 復制代碼

commit

復雜數據結構的父子組件的數據通信用emitv-on事件流容易混亂,尤其是對象嵌套對象的時候。采用 Store 模式,子組件和父組件之間有了 store 這個橋梁,通過 commit 來分發事件

在 commit 函數里面,打上一個console.log,事件的變化全部掌握在你的手里。就像使用 Vuex 一樣

// 定義 FormStore.prototype.commit = function(name, ...args) { const mutations = this.mutations console.log('emit', name) if (mutations[name]) { // states 作為第一個參數 mutations[name].apply(this, [this.states].concat(args)) } else { throw new Error(`Action not found: ${name}`) } } // 分發事件 this.store.commit('setFormItemToHandle', val) 復制代碼

使用

在父組件的 data 里面創建 store,然后把 store 傳入到各個子組件里面去。代碼邏輯非常清楚

data() {
    const store = new FormStore(this); return { store }; }, computed: { form() { return this.store.states.formAttribute; } }, methods: { genFormItem(val) { this.store.commit("setFormItemToHandle", val); } } } 復制代碼

Store 模式 vs EventBus

Vuex 的優點即是 Store 模式的優點

  1. 易於調試與管理
  2. 和 EventBus 差不多的便捷,雖然做不到全局發事件,接受事件,但是如果有這種情況的話,為什么不試試 Vuex 呢
  3. 可局部應用,職責專一

EventBus 在代碼量增多的情況下:

  1. 代碼邏輯性極具下降,可閱讀性變低
  2. 對於每一個 action 父組件都需要一個 on(或 dispatch)一個事件來處理
  3. 你將很難查找到每一個事件是從哪里觸發,滿篇都是業務邏輯

Store 模式 vs Vuex

有的時候,我們可能不知道是否該使用 Vuex,雖然 Redux 的作者 Dan Abramov 的話這么說:

Flux 架構就像眼鏡:您自會知道什么時候需要它

但是我可能只是輕微的近視,不帶眼鏡也可以,但是看東西不太清楚,帶上眼鏡又感覺有點累贅,這個時候就需要我們的 Store 模式

Vuex 負責全局狀態的管理,Store 模式負責局部狀態的交流

Store 模式可以在你寫一個大型組件的時候,單獨在該組件中使用,不用數據都放在 Vuex 里面,作為多個子組件和父組件通信的橋梁使用。我司后台擁有幾十個業務,每個業務下面又會有細分,如果為了寫組件方便,將其放在Vuex里面,那么Vuex將會何等的臃腫

多人開發的時候,每個人負責的業務有單獨的 Store 也不會互相影響

你甚至可以使用多個 Store 去組織你所有的代碼

總結

模仿 Vuex,我們多了一種組織復雜組件或局部狀態管理的新思路,在你寫復雜的組件,又不想污染全局的 Vuex,又需要將狀態在多個組件中共享,則可以考慮一下 Store 模式,和 Vuex 一樣方便,和 EventBus 一樣輕量

既然采用了模仿 Vuex 的方式,代碼風格就要貫徹到底,畢竟 Store 模式沒有強力的約束,不能像 ElementUI 一樣,代碼里面還有直接修改 states 語句(逃

eg: this.store.states.treeData = this.getTableTreeData(value);

體驗我基於 Store 模式改造的 ElementUI 表單編輯器項目,記得點個小星星哦,查看項目地址

參考

關注下面的標簽,發現更多相似文章

作者:sarva
鏈接:https://juejin.im/post/5cd50849f265da03a54c3877
來源:掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM