[譯] 一張圖弄明白 Vuex 里該存放什么樣的數據


原文: 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. 你們要的圖

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

 

 

VI. 總結

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

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



--End--

搜索 fewelife 關注公眾號

轉載請注明出處

 

作者:江米小棗tonylua
鏈接:https://juejin.cn/post/6844903840114966536
來源:稀土掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

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



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