Vuex 简介
Vuex 是Vue.js 应用程序的状态管理模式 + 库。它充当应用程序中所有组件的集中存储,其规则确保状态只能以可预测的方式改变。它还与 Vue 的官方devtools 扩展集成 (打开新窗口)提供高级功能,例如零配置时间旅行调试和状态快照导出/导入。
vuex原理
让我们从一个简单的 Vue 计数器应用程序开始:
new Vue({
// state
data () {
return {
count: 0
}
},
// view
template: `
<div>{{ count }}</div>
`,
// actions
methods: {
increment () {
this.count++
}
}
})
它是一个独立的应用程序,包含以下部分:
- 的状态,真理,推动我们的应用程序的来源;
- 的视图,所述的声明性映射状态;
- 的动作,可能的方式的状态可以从反应于用户输入改变视图。
这是“单向数据流”概念的简单表示:
然而,当我们有多个共享一个公共状态的组件时,这种简单性很快就会崩溃:
- 多个视图可能依赖于同一个状态。
- 来自不同视图的操作可能需要改变相同的状态。
对于问题一,传递 props 对于深度嵌套的组件可能很乏味,而且对于兄弟组件根本不起作用。对于问题二,我们经常发现自己求助于解决方案,例如获取直接的父/子实例引用或尝试通过事件来改变和同步多个状态副本。这两种模式都很脆弱,很快就会导致无法维护的代码。
那么为什么我们不从组件中提取共享状态,并在全局单例中管理它呢?这样,我们的组件树就变成了一个大“视图”,任何组件都可以访问状态或触发动作,无论它们在树中的哪个位置!
通过定义和分离状态管理中涉及的概念并强制执行维护视图和状态之间独立性的规则,我们为我们的代码提供了更多的结构和可维护性。
这是 Vuex 背后的基本思想,受Flux启发 ( 与其他模式不同,Vuex 也是一个专门为 Vue.js 量身定制的库实现,以利用其粒度反应系统进行高效更新。
如果你想以交互方式学习 Vuex,你可以在 Scrimba 上查看这个Vuex 课程 (打开新窗口),它为您提供了截屏视频和代码游乐场的组合,您可以随时暂停和播放。
#我应该什么时候使用它?
Vuex 帮助我们以更多的概念和样板为代价来处理共享状态管理。这是短期和长期生产力之间的权衡。
如果您从未构建过大型 SPA 并直接跳入 Vuex,那可能会感到冗长和令人生畏。这是完全正常的 - 如果您的应用程序很简单,那么您很可能没有 Vuex 也可以。一个简单的存储模式 (打开新窗口)可能就是你所需要的。但是,如果您正在构建一个中大型 SPA,您可能会遇到一些情况,让您考虑如何更好地处理 Vue 组件之外的状态,而 Vuex 将是您的下一步。Redux的作者Dan Abramov有一个很好的报价:
Flux 库就像眼镜:你会知道什么时候需要它们。
Vuex 是 Vue.js 的官方状态管理库。它的工作是在应用程序的组件之间共享数据。开箱即用的 Vue.js 中的组件可以使用
- props,将状态从父组件传递给子组件
- events,从子组件更改父组件的状态,或使用根组件作为事件总线
有时事情会变得比这些简单选项所允许的更复杂。
在这种情况下,一个不错的选择是将状态集中在一个商店中。这就是 Vuex 所做的。
为什么要使用 Vuex
Vuex 不是您可以在 Vue 中使用的唯一状态管理选项(您也可以使用Redux),但它的主要优点是它是官方的,并且它与 Vue.js 的集成使它发光。
使用React,您将不得不选择众多可用库中的一种,因为该生态系统庞大且没有实际标准。最近 Redux 是最受欢迎的选择,MobX 在流行度方面紧随其后。使用 Vue,我什至要说除了 Vuex 之外,您不需要四处寻找任何东西,尤其是在开始时。
Vuex 借鉴了 React 生态系统的许多想法,因为这是 Redux 推广的 Flux 模式。
如果您已经了解 Flux 或 Redux,那么 Vuex 会非常熟悉。如果你不这样做,没问题 - 我会从头开始解释每个概念。
Vue 应用程序中的组件可以有自己的状态。例如,输入框会将输入的数据存储在本地。这非常好,即使使用 Vuex,组件也可以具有本地状态。
你知道当你开始做大量工作来传递一个状态时,你需要像 Vuex 这样的东西。
在这种情况下,Vuex 为状态提供了一个中央存储库,您可以通过要求存储来改变状态。
每个依赖于特定状态部分的组件都将使用 store 上的 getter 访问它,这确保它在事情发生变化时立即更新。
使用 Vuex 会给应用程序带来一些复杂性,因为需要以某种方式设置才能正常工作,但是如果这有助于解决无组织的 props 传递和事件系统,如果过于复杂,可能会变成意大利面条式的混乱,那么它就是一个不错的选择。
开始吧
在这个例子中,我从一个Vue CLI应用程序开始。Vuex 也可以通过直接将其加载到脚本标签中来使用,但由于 Vuex 更适合更大的应用程序,因此您更有可能将它用于更结构化的应用程序,例如您可以使用 Vue 快速启动的应用程序命令行界面。
我使用的示例将放在 CodeSandbox,这是一个很棒的服务,它有一个 Vue CLI 示例,可以在https://codesandbox.io/s/vue 上使用。我建议用它来玩。
到达那里后,单击“添加依赖项”按钮,输入“vuex”并单击它。
现在 Vuex 将列在项目依赖项中。
要在本地安装 Vuex,您可以运行npm install vuex
或yarn add vuex
在项目文件夹中。
创建 Vuex 商店
现在,我们准备创建Vuex商店。
这个文件可以放在任何地方。一般建议把它放在src/store/store.js
文件中,所以我们会这样做。
在这个文件中,我们初始化 Vuex 并告诉 Vue 使用它:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({})
我们导出一个 Vuex 存储对象,我们使用Vuex.Store()
API创建该对象。
商店的用例
现在我们有了一个框架,让我们为 Vuex 的一个好的用例提出一个想法,这样我就可以介绍它的概念。
例如,我有 2 个同级组件,一个带有输入字段,另一个打印输入字段内容。
当输入字段更改时,我还想更改第二个组件中的内容。非常简单,但这将为我们完成工作。
介绍我们需要的新组件
我删除了 HelloWorld 组件并添加了一个 Form 组件和一个 Display 组件。
<template>
<div>
<label for="flavor">Favorite ice cream flavor?</label>
<input name="flavor">
</div>
</template>
<template>
<div>
<p>You chose ???</p>
</div>
</template>
将这些组件添加到应用程序
我们将它们添加到 App.vue 代码而不是 HelloWorld 组件中:
<template>
<div id="app">
<Form/>
<Display/>
</div>
</template>
<script>
import Form from './components/Form'
import Display from './components/Display'
export default {
name: 'App',
components: {
Form,
Display
}
}
</script>
将状态添加到商店
有了这个,我们回到 store.js 文件,我们向名为 的商店添加一个属性state
,它是一个包含该flavor
属性的对象。那最初是一个空字符串。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({
state: {
flavor: ''
}
})
当用户输入输入字段时,我们将更新它。
添加突变
除非使用突变,否则无法操纵状态。我们设置了一个将在 Form 组件中使用的变更来通知商店状态应该改变。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({
state: {
flavor: ''
},
mutations: {
change(state, flavor) {
state.flavor = flavor
}
}
})
添加一个 getter 来引用状态属性
有了这个集合,我们需要添加一种查看状态的方法。我们使用getter 来做到这一点。我们为该flavor
属性设置了一个 getter :
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({
state: {
flavor: ''
},
mutations: {
change(state, flavor) {
state.flavor = flavor
}
},
getters: {
flavor: state => state.flavor
}
})
注意getters
对象如何。flavor
是这个对象的一个属性,它接受状态作为参数,并返回flavor
状态的属性。
将Vuex商店添加到应用程序
现在该商店已准备就绪,可以使用了。我们回到我们的应用程序代码,在 main.js 文件中,我们需要导入状态并使其在我们的 Vue 应用程序中可用。
我们增加
import { store } from './store/store'
我们将它添加到 Vue 应用程序中:
new Vue({
el: '#app',
store,
components: { App },
template: '<App/>'
})
一旦我们添加了它,因为这是主要的 Vue 组件,store
每个 Vue 组件内的变量都将指向 Vuex 存储。
使用提交更新用户操作的状态
让我们在用户输入内容时更新状态。
我们通过使用store.commit()
API来做到这一点。
但首先,让我们创建一个在输入内容更改时调用的方法。我们使用@input
而不是@change
因为后者仅在焦点移离输入框时触发,而@input
在每次按键时调用。
<template>
<div>
<label for="flavor">Favorite ice cream flavor?</label>
<input @input="changed" name="flavor">
</div>
</template>
<script>
export default {
methods: {
changed: function(event) {
alert(event.target.value)
}
}
}
</script>
现在我们有了风味的价值,我们使用 Vuex API:
<script>
export default {
methods: {
changed: function(event) {
this.$store.commit('change', event.target.value)
}
}
}
</script>
看看我们如何使用this.$store
? 这要归功于在主要Vue组件初始化中包含了存储对象。
该commit()
方法接受一个突变名称(我们change
在Vuex存储中使用了)和一个有效负载,这些负载将作为回调函数的第二个参数传递给该突变。
使用 getter 打印状态值
现在我们需要在 Display 模板中引用这个值的 getter,使用$store.getters.flavor
. this
可以删除,因为我们在模板中,并且this
是隐式的。
<template>
<div>
<p>You chose {{ $store.getters.flavor }}</p>
</div>
</template>
包起来
Vuex 的介绍到此结束!