[譯] 一張圖弄明白 Vuex 裡該存放什麼樣的資料

江米小棗tonylua發表於2019-05-08

原文: markus.oberlehner.net/blog/should…

大多數人剛上手 Vuex 的時候,首先都想知道,應該往其中存放什麼樣的資料呢?在對這個問題給出答案的過程中,很多人(包括我)先是來到了“一股腦放進去”的階段。但是在遭遇了首次障礙後,你很快就會領悟到:這可不是在 Vue.js 應用中管理資料的完美方案啊。

在本文中我將嘗試回答諸如“Vuex 在何種情景下是個稱手的解決方案”,以及“何時用其他方式更好些”的這類問題。

I. 首先,為何使用 Vuex ?

Vue.js 為我們提供了響應式的 data 屬性 -- 這是一種開箱即用的處理狀態的強大方式,也能向子元件中傳遞資料。

export default {
  name: 'MyComponent',
  data() {
    return {
      someValue: 'Hello World',
    };
  },
}
複製程式碼
<template>
  <div class="MyComponent">
    <some-component :some-value="someValue"></some-component>
  </div>
</template
複製程式碼

如果在開發一個相當簡單的應用,或者你要做的全部事情就是利用 Vue.js 的某些魔力來替換應用的一些(原本是服務端渲染)部分,那麼你確實根本用不著 Vuex。

反之,如果要開發一個大體量的單頁應用,你就可能遇到在應用中的兩處迥然不同的地方需要同樣一份資料的狀況。這就是像 Vuex 這樣的集中式狀態管理工具時不時起到大作用的時候了。

II. 把資料存入 Vuex 的理由

那麼把資料存入一個集中式的 Vuex store 中有哪些理由呢?

2-1. 資料對多個(獨立的)元件來說必須是可訪問的

把資料放在 Vuex 這種集中式 store 裡面的第一個用例,那就是,因為資料必須被應用中的多個地方訪問到,而這些地方很可能是毫不相干(並不是父元件子元件那麼簡單)的若干元件。一個例子是用某些自定義設定項去配置應用的外觀或在具體的某處應該使用什麼日期格式。

2-2. 集中式的 API / 資料獲取邏輯

我們還是搬出久經考驗的 To-Do 應用作為例子:你要從一個 API 中請求得到包含所有 To-Do 項的列表,又要按時間排序顯示所有專案,也有頁面是隻顯示其中的特定分類的。藉助 Vuex,你可以只獲取一次全部 To-Do 項並儲存在 store 中,然後在應用中的每個元件中訪問這些資料,哪怕它們分佈在不同路由中也行。另一種方法是當使用者導航到特定分類的路由時再請求特定的 To-Do 項;根據應用的性質,這可能也說得通。

2-3. 客戶端的持久化應用狀態

感謝 vuex-persistedstate 這樣的 Vue.js 外掛,在瀏覽器中用 Vuex 管理持久化狀態變得非常容易了。這使得處理使用者保持離線這樣的複雜狀況變得簡單。

III. 不把資料存入 Vuex 的理由

如果你已經決定了使用 Vuex 管理應用中的狀態,那麼每次增加一個新元件,你就得做一次是否將它的狀態存入 Vuex 的判斷。如果你是 Vuex 的新手,很可能會禁不住用 Vuex 做所有的事 -- 既然有這個玩意,為啥不用呢?

3-1. 複雜性

儘管 Vuex 比其同類工具更簡單些,但相比於直接使用元件本地的狀態還是太麻煩了。你要評估其額外的複雜度和集中式狀態帶來的好處哪個更值得。

3-2. 維護成本

在元件中使用 Vuex 總是意味著有維護成本的。基於此,我推薦你將使用元件的本地狀態作為預設項,而只在有充分理由時才選擇性的用 Vuex。

IV. Vuex 之外的儲存資料替代方案

既然說 Vuex 有那麼些的缺點,那麼當我們判斷其並非最佳方案時有哪些替代品呢?

4-1. 向下傳遞的 props

往往最簡單的方法就是最好的方法。如果能用從父元件向子元件傳遞的 props 解決問題,你就絕對應該那麼幹。

4-2. provide / inject

一個少有人知的 Vue.js 特性是 provide / inject。它用於需要從一個祖先元件向其所有子孫元件傳遞資料的場景。

官方文件中的基礎示例:

// 父級元件提供 'foo'
var Provider = {
  provide: {
    foo: 'bar'
  },
  // ...
}

// 子孫元件注入 'foo'
var Child = {
  inject: ['foo'],
  created () {
    console.log(this.foo) // => "bar"
  }
  // ...
}
複製程式碼

一個典型的例子是 accordion 元件,可能由一個主要的 AppAccordion 元件、表示每個摺疊項的若干 AppAccordionItem 子元件,及表示摺疊項主體的 AppAccordionBody 孫元件組成。provide / inject 使得從主元件向孫元件傳遞資料成為可能。在各級元件直接互相依賴的情形下(AppAccordionBody 在脫離 AppAccordion 元件的情況下無法使用),這種模式比起使用 Vuex 來簡單又高效。

4-3. 從 API / Apollo 獲取資料

讓我們回顧一個 2-2 中提及的 Vuex 正面例證:有著多個分類的 To-Do 應用。其實相比於一次性獲取並儲存一個使用者所有(未完成的)To-Do 項,更好的一種實現可能是隻獲取開頭的 20 條用於入口頁面的渲染。若使用者導航到了特定分類頁面,則觸發一次新的請求,以從 API 中獲取對應分類的開頭 20 條。如果使用者訪問了之前開啟過的分類,我們既可以重新請求一次新鮮的資料,也可以實現某種快取(Apollo 就提供了開箱即用的快取機制)。

譯註:GraphQL 是由 Facebook 創造的用於描述複雜資料模型的一種查詢語言,是一種用於前後端資料查詢方式的規範。Apollo 是基於 GraphQL 的全棧解決方案集合。從後端到前端提供了對應的 lib 使得開發使用 GraphQL 更加的方便;一個可用於 Vue 的外掛是 vue-apollo.netlify.com/

4-4. portals

乍一看,PortalVue 外掛貌似和狀態管理怎麼也扯不上關係。但有些狀況下一個 portal 可以直接訪問元件的狀態,而不用通過集中式的 store。一種典型的例子可能是個 modal 對話方塊,用來確認使用者不是誤觸了刪除按鈕:

<template>
  <button class="AppDeleteButton" @click="modal = true">
    刪除
    <portal to="modals" v-if="modal">
      <app-modal>
        你可想好了啊?
        <button @click="delete(item.id)"></button>
        <button>算了</button>
      </app-modal>
    </portal>
  </button>
</template
複製程式碼

可見當 AppDeleteButton 元件被點選時,就顯示其包含的 modal。相比於不使用 ProtalVue 外掛時要分離書寫按鈕和彈窗並通過 store 全域性訪問 id 資料,例子中這種方式就能直接在 model 元件中訪問 AppDeleteButton 的內部屬性值了。

V. 你們要的圖

為了便於決策,將以上內容總結為下圖:

[譯] 一張圖弄明白 Vuex 裡該存放什麼樣的資料

VI. 總結

記住在軟體開發中沒有放之四海而皆準的完全之策。每件事都有各自的情景,很多文章中的某種技術能在特定情況下工作良好,但對於你的特殊用例可能也玩不轉。

要對新的(也包括舊的)處事方法保持開放的胸襟,也不要懼怕嘗試 -- 即便在應用中共享狀態的某些方法並不適用,至少你也學到了何時不去用它,並且任何時候都是重構程式碼的好時候。



--End--

[譯] 一張圖弄明白 Vuex 裡該存放什麼樣的資料

搜尋 fewelife 關注公眾號

轉載請註明出處

相關文章