如何使用 Vuex 管理 Vue.js 应用程序中的状态

作者选择了 开源精神疾病以作为 写给捐赠计划的一部分获得捐赠。

介绍

Vuex是由Evan You创建的第一方开发状态管理库,目前由Vue.js核心团队维护,与许多其他国家管理库一样,Vuex遵循了过去几年中Redux(https://redux.js.org/)普及的原则:数据流向一个方向,行动和突变在一个单一的真理来源中修改数据,称为商店。

Vuex store 是不同的方法和数据的集合,其中一些方法,例如 actions,可以在发送到突变之前获取和处理数据。 mutation 是一种方法,通过提供的值来突变或更新商店属性。 Getters 是可以修改或组合数据来创建一个新的状态属性的方法。 这些 getters 只能读取并且不会改变数据。 这些方法类似于 Vue.js 组件中的计算属性。 Vuex 的最后一个组件是 state 或作为您的单一真理来源的数据集。

在本教程中,您将创建一个应用程序,该应用程序将显示有机场信息的卡片列表。在点击时,这些卡片将执行Vuex工作流程,将所选的机场添加到喜爱的列表中。

前提条件

  • Node.js 版本 14.16.0’ 或更高版本安装在您的计算机上。 要在 macOS 或 Ubuntu 20.04 上安装此功能,请按照 [How To Install Node.js and Create a Local Development Environment on macOS](https://andsky.com/tech/tutorials/how-to-install-node-js-and-create-a-local-development-environment-on-macos) 或 [How To Install Node.js on Ubuntu 20.04](https://andsky.com/tech/tutorials/how-to-install-node-js-on-ubuntu-20-04) 部分的 **Installing Using a PPA** 选项(https://andsky.com/tech/tutorials/how-to-install-node-js-and-create-a-local-development-environment-on-macos) 的步骤,在您的计算机上执行 [How To Install Node.js and Create a Local Development Environment on macOS](https://andsky.com/tech/tutorials/how-to-install-node-js-and-create-a-local-development-environment-on-macos) 并且新生成的项目。 请确保在生成应用程序时选择Default (Vue 3 Preview) 选项。 该项目的名称将是

步骤 1 – 设置示例应用程序

为了帮助您可视化如何使用 Vuex 管理状态,请设置一个包含某些数据的项目,以便在视图中显示。

一旦创建了最喜欢的机场项目,如在前提条件部分中所述,请创建一个目录,以存储该项目的所有本地数据。

1mkdir src/data
2touch src/data/airports.js

这将创建一个数据目录和一个空的airports.js文件。

在您所选择的文本编辑器中,打开新创建的 airports.js 文件,并添加以下内容:

 1[label favorite-airports/src/data/airports.js]
 2export default [
 3  {
 4    name: 'Cincinnati/Northern Kentucky International Airport',
 5    abbreviation: 'CVG',
 6    city: 'Hebron',
 7    state: 'KY'
 8  },
 9  {
10    name: 'Seattle-Tacoma International Airport',
11    abbreviation: 'SEA',
12    city: 'Seattle',
13    state: 'WA',
14  },
15  {
16    name: 'Minneapolis-Saint Paul International Airport',
17    abbreviation: 'MSP',
18    city: 'Bloomington',
19    state: 'MN',
20  },
21  {
22    name: 'Louis Armstrong New Orleans International Airport',
23    abbreviation: 'MSY',
24    city: 'New Orleans',
25    state: 'LA',
26  },
27  {
28    name: `Chicago O'hare International Airport`,
29    abbreviation: 'ORD',
30    city: 'Chicago',
31    state: 'IL',
32  },
33  {
34    name: `Miami International Airport`,
35    abbreviation: 'MIA',
36    city: 'Miami',
37    state: 'FL',
38  }
39]

这是由美国几个机场组成的 object的数组(https://andsky.com/tech/tutorials/understanding-arrays-in-javascript)。 在此应用程序中,您将通过这些数据进行迭代,以生成包括名称,缩写,城市属性的卡片。当用户点击卡片时,您将执行发送方法,将该机场添加到您的Vuex状态作为您最喜欢的机场。

保存数据/airports.js并返回终端。

完成此步骤后,创建一个名为AirportCard.vue的单个文件组件(SFC)。该文件将生活在您的项目的组件目录中。该组件将包含机场卡的所有风格和逻辑。在您的终端中,使用触摸命令创建.vue文件:

1touch src/components/AirportCard.vue

在文本编辑器中打开AirportCard.vue,并添加以下内容:

 1[label favorite-airports/src/components/AirportCard.vue]
 2<template>
 3  <div class="airport">
 4    <p>{{ airport.abbreviation }}</p>
 5    <p>{{ airport.name }}</p>
 6    <p>{{ airport.city }}, {{ airport.state }}</p>
 7  </div>
 8</template>
 9
10<script>
11export default {
12  props: {
13    airport: {
14      type: Object,
15      required: true
16    }
17  }
18}
19</script>
20
21<style scoped>
22.airport {
23  border: 3px solid;
24  border-radius: .5rem;
25  padding: 1rem;
26  margin-bottom: 1rem;
27}
28
29.airport p:first-child {
30  font-weight: bold;
31  font-size: 2.5rem;
32  margin: 1rem 0;
33}
34
35.airport p:last-child {
36  font-style: italic;
37  font-size: .8rem;
38}
39</style>

AirportCard.vue组件中,包装器<div>包含了机场的类别。本CSS通过添加边界来为每个机场提供卡片的外观,为生成的HTML添加了一些风格。:第一个孩子:最后一个孩子是应用于div中的HTML中第一个和最后一个p标签的不同风格的pseudo选择器。

保存和退出文件。

在包装安装之前,请用以下代码更换现有App.vue组件:

 1[label favorite-airports/src/App.vue]
 2<template>
 3  <div class="wrapper">
 4    <div v-for="airport in airports" :key="airport.abbreviation">
 5      <airport-card :airport="airport" />
 6    </div>
 7  </div>
 8</template>
 9
10<script>
11import { ref } from 'vue'
12import allAirports from '@/data/airports.js'
13import AirportCard from '@/components/AirportCard.vue'
14
15export default {
16  components: {
17    AirportCard
18  },
19  setup() {
20    const airports = ref(allAirports)
21    return { airports }
22  }
23}
24</script>
25
26<style>
27#app {
28  font-family: Avenir, Helvetica, Arial, sans-serif;
29  -webkit-font-smoothing: antialiased;
30  -moz-osx-font-smoothing: grayscale;
31  text-align: center;
32  color: #2c3e50;
33  margin-top: 60px;
34}
35
36.wrapper {
37  display: grid;
38  grid-template-columns: 1fr 1fr 1fr;
39  grid-column-gap: 1rem;
40  max-width: 960px;
41  margin: 0 auto;
42}
43
44p,
45h3 {
46  grid-column: span 3;
47}
48</style>

此代码包含一个 v-for 循环,它通过 airports.js 数据进行迭代,并通过 prop :airport 传输的机场数据,返回一系列 AirportCards.vue 组件。

在项目设置时,使用终端中的npm run serve命令运行本地开发服务器:

1npm run serve

这将启动您的本地主机上的服务器,通常在端口8080,打开您所选择的网页浏览器,然后访问本地主机:8080,以查看以下内容:

A list of cards rendered in Vue using the v-for directive.

现在你的示例应用程序已设置,在下一步,你将安装Vuex库,并创建一个商店。这个商店是一个收藏的几个不同的Vuex项目,包括:状态,突变,行动,和Getters。

第2步:安装Vuex

当您在基于 Web 的应用程序上工作时,您通常会使用 state 工作。 状态是特定时间的数据集合。 这种状态可以通过发送承诺方法通过用户交互来更改。

有些开发人员跳过动作并直接进入参数。然而,为了这个教程,你总是会执行一个动作,这反过来叫做突变。这样你就可以在动作中有多个突变。Vuex的基本规则是,突变只有一项工作,只有一项工作:更新商店。动作可以做很多不同的事情,包括组合数据、收集数据和运行JavaScript逻辑。

除了操作之外,还有 getters。一个 getter 是将多个状态值合并为一个值的一种方法. 如果您熟悉 Vue.js 中的计算属性,则 getters 可以被认为是特定状态的计算属性。

使用 Vuex 术语,开始安装和集成 Vuex. 打开终端并执行以下命令:

1npm install vuex@next --save

此命令将安装与 Vue.js 3.x 最兼容的 Vuex 版本,并将其保存到您的 package.json 文件中。接下来,为您的商店创建一个目录和索引文件。

1mkdir src/store
2touch src/store/index.js

打开文本编辑器,在您的store/index.js文件中,开始您的Vuex商店。 要做到这一点,您需要利用Vuex的createStore函数:

1[label airport-favorites/src/store/index.js]
2import { createStore } from 'vuex'
3
4export default createStore({
5
6})

您还将导出,因为您将在以后将其导入到您的main.js 文件中。

此时,您已设置了 Vuex 商店,但应用程序尚未了解它或如何使用它. 要完全初始化商店,请将其导入到您的 main.js 文件中。

立即在「createApp(App)」之後,連鎖「使用」方法並傳入您正在輸入的商店,如下標示的代碼所示:

1[label favorite-airports/src/main.js]
2import { createApp } from 'vue'
3import App from './App.vue'
4import store from './store'
5
6createApp(App).use(store).mount('#app')

一旦你链接了使用方法,保存此文件。使用方法告诉Vue应用程序在构建应用程序时要组合哪个代码。

在您进入下一个部分之前,请在App.vue文件中添加状态值,并引用它。

 1[label favorite-airports/src/store/index.js]
 2import { createStore } from 'vuex'
 3
 4export default createStore({
 5state: {
 6    firstName: 'John',
 7    lastName: 'Doe'
 8  },
 9mutations: {
10
11},
12actions: {
13
14},
15getters: {
16
17}
18})

这些属性反映了商店所持有的数据类型:状态是指状态(全球数据),突变是指数据突变的承诺,行动是指突变的发送,getters是指商店的计算属性。

保存store/index.js,然后在文本编辑器中打开App.vue文件,并添加以下内容:

 1[label favorite-airports/src/App.vue]
 2<template>
 3  <div class="wrapper">
 4    <p>{{ $store.state.firstName }} {{ $store.state.lastName }}</p>
 5    <div v-for="airport in airports" :key="airport.abbreviation">
 6      <airport-card :airport="airport" />
 7    </div>
 8  </div>
 9</template>
10...

在这种情况下,$store是您在main.js文件中初始化的全球商店. 如果您将this.$store登录到控制台,您将看到商店object

保存App.vue,然后打开您的网页浏览器。在机场卡上,您将看到您在Vuex商店中保存的姓名和姓名。

Data from the Vuex store displayed in the view.

在此步骤中,您安装了 Vuex 并创建了一个 Vuex 商店. 您添加了一些默认的商店数据,并使用点概念在$store对象视图中显示它. 在下一步中,您将通过行动突变更新您的 Vuex 商店,并将与getters组合数据。

步骤 3 – 创建行动,突变和接收器

在第 2 步中,您手动安装了 Vuex 并将其集成到您的项目中。在此步骤中,您仍然在浏览器中渲染了您的姓名和姓名,但您将创建一个 Vuex getter 来渲染数据作为一个字符串。

要创建一个ghetter,请在所选的文本编辑器中打开src/store/index.js文件。一旦打开,请在getters对象中创建一个具有函数值的属性。

添加以下突出的代码:

 1[label favorite-airports/src/store/index.js]
 2import { createStore } from 'vuex'
 3
 4export default createStore({
 5  state: {
 6    firstName: 'John',
 7    lastName: 'Doe'
 8  },
 9  ...
10  getters: {
11    fullName: function () {
12
13    }
14  }
15})

在这种情况下,您将使用该函数组合第一名和最后名称,并将结果的属性存储为fullName。在函数内部,您将需要输入位于 Vuex 商店内的状态对象。

 1[label favorite-airports/src/store/index.js]
 2import { createStore } from 'vuex'
 3
 4export default createStore({
 5  state: {
 6    firstName: 'John',
 7    lastName: 'Doe'
 8  },
 9  ...
10  getters: {
11    fullName: function (state) {
12      return `${state.firstName} ${state.lastName}`
13    }
14  }
15})

您正在使用模板字母在这里将FirstNameLastName放在一个字符串中。

保存此文件,然后返回App.vue。在此文件中,删除第一个和最后一个值,并用getter替换它们:

 1[label favorite-airports/src/App.vue]
 2<template>
 3  <div class="wrapper">
 4    <p>{{ $store.getters.fullName }}</p>
 5    <div v-for="airport in airports" :key="airport.abbreviation">
 6      <airport-card :airport="airport" />
 7    </div>
 8  </div>
 9</template>
10...

一旦你做這個變更並保存檔案,你的瀏覽器將 熱重載。你會像以前一樣在你的瀏覽器中看到你的名字和姓名,但現在你正在利用ghetter。

正如在最后一步中提到的,为了这个教程,你总是会使用一个操作,而不是直接突变数据。

在这个项目中,当用户点击该卡时,您将将机场的数据添加到最喜欢列表中,您将首先获得创建的动作和突变,然后使用v-on指令将其分配给一个点击事件。

要创建一个动作,请在文本编辑器中打开src/store/index.js文件,在商店的动作部分创建一个函数,就像getter一样,函数名称将是您后来引用该动作的方式。

 1[label favorite-airports/src/store/index.js]
 2import { createStore } from 'vuex'
 3
 4export default createStore({
 5  state: {
 6    firstName: 'John',
 7    lastName: 'Doe',
 8    favorites: [] // will store favorites here
 9  },
10  mutations: {
11
12  },
13  actions: {
14    addToFavorites() {
15
16    }
17  },
18  getters: {
19    fullName: function (state) {
20      return `${state.firstName} ${state.lastName}`
21    }
22}
23})

一个动作接受两个参数:背景,或Vue应用程序本身,和负载,或您想要添加到商店的数据。

 1[label favorite-airports/src/store/index.js]
 2import { createStore } from 'vuex'
 3
 4export default createStore({
 5  state: {
 6    firstName: 'John',
 7    lastName: 'Doe',
 8    favorites: []
 9  },
10  mutations: {
11
12  },
13  actions: {
14    addToFavorites(context, payload) {
15      context.commit('UPDATE_FAVORITES', payload)
16    }
17  },
18  getters: {
19    fullName: function (state) {
20      return `${state.firstName} ${state.lastName}`
21    }
22 }
23})

委托方法还接受两个论点:要调用的突变的名称和负载或突变将取代状态的数据。

在这个代码中,你正在命名突变UPDATE_FAVORITES。 突变名应该是无知的,而不是根据特定操作命名。 例如,像ADD_FAVORITEREMOVE_FAVORITE这样的突变意味着一个逻辑,比如删除或添加数据片段。 这不是理想的,因为突变应该有一个工作和一个工作:更新状态。 要区分添加和删除数据,你可以有两个不同的行动,从数组中删除或添加一个最喜欢的机场,然后执行一个单一的突变,名为UPDATE_FAVORITES,以更新数组与任何经过的内容。 尽量减少你在你的商店中有多少突变将有助于你的Vuex商店更容易管理它,因为它

接下来,为此操作添加一些逻辑。当您将机场添加为最喜欢时,您将将该效用量(机场数据)添加到现有数组中。

 1[label favorite-airports/src/store/index.js]
 2import { createStore } from 'vuex'
 3
 4export default createStore({
 5  state: {
 6    firstName: 'John',
 7    lastName: 'Doe',
 8    favorites: []
 9  },
10  mutations: {
11
12  },
13  actions: {
14    addToFavorites(context, payload) {
15      const favorites = context.state.favorites
16      favorites.push(payload)
17      context.commit('UPDATE_FAVORITES', favorites)
18    }
19  },
20  getters: {
21    fullName: function (state) {
22      return `${state.firstName} ${state.lastName}`
23    }
24 }
25})

在此时,您的操作将设置为将盈利负载添加到您的喜爱数组中,然后将具有变异数组的突变称为新数据。

 1[label favorite-airports/src/store/index.js]
 2import { createStore } from 'vuex'
 3
 4export default createStore({
 5  state: {
 6      firstName: 'John',
 7    lastName: 'Doe',
 8    favorites: []
 9    },
10  mutations: {
11    UPDATE_FAVORITES(state, payload) {
12      state.favorites = payload
13    }
14  },
15  actions: {
16    addToFavorites(context, payload) {
17      const favorites = context.state.favorites
18      favorites.push(payload)
19      context.commit('UPDATE_FAVORITES', favorites)
20    }
21  },
22  getters: {
23    fullName: function (state) {
24      return `${state.firstName} ${state.lastName}`
25    }
26 }
27})

现在你有你的行动和突变,你可以保存这个文件。

要执行此操作,您可以在用户点击卡片时调用发送事件,您将使用v-on指令这样做。

在您的文本编辑器中打开App.vue文件. 在<airport-card />组件上,添加v-on指令的短语语法(@)以事件为点击:

 1[label favorite-airports/src/App.vue]
 2<template>
 3  <div class="wrapper">
 4    <p>{{ $store.getters.fullName }}</p>
 5    <div v-for="airport in airports" :key="airport.abbreviation">
 6      <airport-card :airport="airport" @click="$store.dispatch('addToFavorites', airport)" />
 7    </div>
 8    <h2 v-if="$store.state.favorites.length">Favorites</h2>
 9    <div v-for="airport in $store.state.favorites" :key="airport.abbreviation">
10      <airport-card :airport="airport"  />
11    </div>
12  </div>
13</template>
14...

发送函数接受两个参数:该动作的名称和您发送到该动作的负载数据。

现在,当你点击机场地图时,行动将调用变异,更新状态并将机场添加到喜爱的财产。

A favorite airport added to the favorite airports section after a Vuex mutation was executed.

在此步骤中,您扩展了您之前创建的 Vuex 商店. 您创建了一项操作,该操作复制一个数组并将新项目推向该数组. 该操作被称为突变,从而更新了状态。

在最后一步中,您将部署Vuex模块。模块是将您的Vuex商店分割成更小的Vuex商店的绝佳方式。

步骤 4 – 构建 Vuex 模块

模块是较小的Vuex商店,这些商店被合并成一个Vuex商店,这类似于多种Vue.js组件被导入一个单一的.vue 文件,如 App.vue。在这个步骤中,您将把这个Vuex商店分为两个单独的模块。一个模块将用于用户状态,另一个模块将针对机场状态、行动和突变。

在您的终端中,进入商店目录,并使用触摸命令创建两个单独的文件。

1touch src/store/user.module.js
2touch src/store/airports.module.js

user.module.js 文件中,通过添加以下代码创建一个将默认导出的对象:

1[label favorite-airports/src/store/user.module.js]
2export default {
3  namespaced: true
4}

您还将添加名称空间属性,其值为名称空间属性将这样做,以便您在稍后访问有点符号的属性时可以参考模块名称。

在此对象中,您将添加与用户相关的状态接收信息:

 1[label favorite-airports/src/store/user.module.js]
 2export default {
 3  namespaced: true,
 4  state: {
 5    firstName: 'John',
 6    lastName: 'Doe'
 7  },
 8  getters: {
 9    fullName: function (state) {
10      return `${state.firstName} ${state.lastName}`
11    }
12  }
13}

用户模块包含所有您需要的用户信息. 保存和退出文件。

在您的文本编辑器中打开airports.module.js文件并添加以下内容:

 1[label favorite-airports/src/store/airports.module.js]
 2export default {
 3  state: {
 4    favorites: []
 5  },
 6  mutations: {
 7    UPDATE_FAVORITES(state, payload) {
 8      state.favorites = payload
 9    }
10  },
11  actions: {
12    addToFavorites(context, payload) {
13      const favorites = context.state.favorites
14      favorites.push(payload)
15      context.commit('UPDATE_FAVORITES', favorites)
16    }
17  },
18}

现在你已经把机场相关的突变,行动和状态,你可以保存你的airports.module.js

接下来,将这两个文件导入到主store/index.js文件中,在文本编辑器中打开store/index.js文件,然后删除state,mutations,actionsgetters属性。

1[label favorite-airports/src/store/index.js]
2import { createStore } from 'vuex'
3
4export default createStore({
5
6})

要注册模块,您需要将它们导入到这个index.js文件中,并使用以下突出代码:

1[label favorite-airports/src/store/index.js]
2import { createStore } from 'vuex'
3import UserModule from './user.module.js'
4import AirportsModule from './airports.module.js'
5
6export default createStore({
7
8})

从这里开始,您需要有一个名为模块的属性,其值为一个对象。该对象内部的属性名称将是Vuex模块的名称。

 1[label favorite-airports/src/store/index.js]
 2import { createStore } from 'vuex'
 3import UserModule from './user.module.js'
 4import AirportsModule from './airports.module.js'
 5
 6export default createStore({
 7modules: {
 8  user: UserModule,
 9  airports: AirportsModule
10}
11})

一旦您保存了此文件,您的模块现在已注册并合并到您的单一Vuex商店。保存商店/index.js,然后打开App.vue文件并更新它以参考新创建的模块:

 1[label favorite-airports/src/App.vue]
 2<template>
 3  <div class="wrapper">
 4    <p>{{ $store.getters['user/fullName'] }}</p>
 5    <div v-for="airport in airports" :key="airport.abbreviation">
 6      <airport-card :airport="airport" @click="$store.dispatch('addToFavorites', airport)" />
 7    </div>
 8    <h2 v-if="$store.state.airports.favorites.length">Favorites</h2>
 9    <div v-for="airport in $store.state.airports.favorites" :key="airport.abbreviation">
10      <airport-card :airport="airport"  />
11    </div>
12  </div>
13</template>
14...

现在你有一个模块化版本的Vuex设置。

在此步骤中,您将现有的Vuex商店分割成更小的块,称为模块,这些模块是将相关商店属性组合为更小的Vuex商店的绝佳方法,您还更新了App.vue,以参考每个模块中的状态和发送事件。

结论

在这个设置中,状态中的数据在整个应用程序中都是全球性的,作为一个单一的真理来源,只能以行动和突变的形式进行明确的功能更新。

要了解更多关于 Vuex 的操作、突变和模块,请查看 Vue.js 核心团队撰写的 官方 Vuex 文档 有关 Vue 的更多教程,请参阅 如何使用 Vue.js 系列开发网站页面

Published At
Categories with 技术
comments powered by Disqus