[译] 一张图弄明白 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 关注公众号

转载请注明出处