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

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

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

在本文中我將嘗試回答諸如「Vuex 在何種情景下是個稱手的解決方案」,以及「什麼時候用其餘方式更好些」的這類問題。前端

I. 首先,爲什麼使用 Vuex ?

Vue.js 爲咱們提供了響應式的 data 屬性 -- 這是一種開箱即用的處理狀態的強大方式,也能向子組件中傳遞數據。vue

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。java

反之,若是要開發一個大致量的單頁應用,你就可能遇到在應用中的兩處迥然不一樣的地方須要一樣一份數據的情況。這就是像 Vuex 這樣的集中式狀態管理工具時不時起到大做用的時候了。git

II. 把數據存入 Vuex 的理由

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

2-1. 數據對多個(獨立的)組件來講必須是可訪問的

把數據放在 Vuex 這種集中式 store 裏面的第一個用例,那就是,由於數據必須被應用中的多個地方訪問到,而這些地方極可能是絕不相干(並非父組件子組件那麼簡單)的若干組件。一個例子是用某些自定義設置項去配置應用的外觀或在具體的某處應該使用什麼日期格式。vuex

2-2. 集中式的 API / 數據獲取邏輯

咱們仍是搬出久經考驗的 To-Do 應用做爲例子:你要從一個 API 中請求獲得包含全部 To-Do 項的列表,又要按時間排序顯示全部項目,也有頁面是隻顯示其中的特定分類的。藉助 Vuex,你能夠只獲取一次所有 To-Do 項並存儲在 store 中,而後在應用中的每一個組件中訪問這些數據,哪怕它們分佈在不一樣路由中也行。另外一種方法是當用戶導航到特定分類的路由時再請求特定的 To-Do 項;根據應用的性質,這可能也說得通。後端

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

感謝 vuex-persistedstate 這樣的 Vue.js 插件,在瀏覽器中用 Vuex 管理持久化狀態變得很是容易了。這使得處理用戶保持離線這樣的複雜情況變得簡單。api

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 關注公衆號

轉載請註明出處

相關文章
相關標籤/搜索