Vuex是Vue.js的官方状态管理库,用于管理应用程序的状态。本篇博客将介绍如何安装Vuex、创建Vuex Store,并在Vue应用中使用Vuex来访问和修改状态。同时,我们将提供一个实例演示,以更具体地展示Vuex的用法。
首先,我们使用npm或yarn在项目的根目录下安装Vuex:
```
npm install vuex
```
在项目中创建一个新的JavaScript文件,例如store.js
,并编写以下代码:
```js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++
}
},
actions: {
increment(context) {
context.commit('increment')
}
},
getters: {
doubleCount(state) {
return state.count * 2
}
}
})
export default store
```
在Vue应用的入口文件(一般是main.js
)中导入并使用Vuex Store:
```javascript
import Vue from 'vue'
import App from './App.vue'
import store from './store'
new Vue({
store,
render: h => h(App)
}).$mount('#app')
```
现在,我们可以在组件中访问和修改Vuex Store中的状态。在计算属性和方法中使用this.$store.state
来访问状态,使用this.$store.commit()
来触发状态的变更。
<template>
<div>
<p>Count: {{ count }}p>
<p>Double Count: {{ doubleCount }}p>
<button @click="increment">Incrementbutton>
div>
template>
<script>
export default {
computed: {
count() {
return this.$store.state.count
},
doubleCount() {
return this.$store.getters.doubleCount
}
},
methods: {
increment() {
this.$store.dispatch('increment')
}
}
}
script>
在上述示例中,我们使用this.$store.state.count
访问状态,使用this.$store.getters.doubleCount
获取计算属性的值,通过this.$store.dispatch('increment')
来触发状态的变更。
当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键:
在组件的computed选项中,使用mapState函数将Vuex状态映射为计算属性。你可以传递一个数组或者对象给mapState函数。
当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组。
computed: mapState(['count'])
computed: mapState({
count: 'count'
})
上述两种语法只能提供映射,不能计算.如果想要实现计算,需要这样写:
computed: {
...mapState(['count']),
doubleCount() {
return this.count * 2
}
}
computed: mapState({
count: state => state.count,
// 或者使用箭头函数
doubleCount: state => state.count * 2
})
上述代码可以对状态进行更高级的处理,例如对状态进行计算或者过滤。
// 在单独构建的版本中辅助函数为 Vuex.mapState
import { mapState } from 'vuex'
export default {
// ...
computed: mapState({
// 箭头函数可使代码更简练
count: state => state.count,
// 传字符串参数 'count' 等同于 `state => state.count`
countAlias: 'count',
// 为了能够使用 `this` 获取局部状态,必须使用常规函数
countPlusLocalState (state) {
return state.count + this.localCount
}
})
}
有时候我们需要从 store 中的 state 中派生出一些状态,例如对列表进行过滤并计数:
computed: {
doneTodosCount () {
return this.$store.state.todos.filter(todo => todo.done).length
}
}
如果有多个组件需要用到此属性,我们要么复制这个函数,或者抽取到一个共享函数然后在多处导入它——无论哪种方式都不是很理想。
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: 'Buy groceries', completed: false },
{ id: 2, text: 'Clean the house', completed: true },
{ id: 3, text: 'Walk the dog', completed: false }
]
},
getters: {
// Getter用于计算未完成的任务数量
unfinishedTasks(state) {
return state.todos.filter(todo => !todo.completed).length
}
}
})
在上述示例中,我们定义了一个名为unfinishedTasks的Getter,用于计算未完成的任务数量。
<template>
<div>
<p>Unfinished Tasks: {{ unfinishedTasks }}p>
div>
template>
<script>
export default {
computed: {
unfinishedTasks() {
return this.$store.getters.unfinishedTasks
}
}
}
script>
在上述示例中,我们在组件的计算属性中使用this.$store.getters.unfinishedTasks来获取未完成的任务数量。
<template>
<div>
<p>Unfinished Tasks: {{ unfinishedTasks }}p>
div>
template>
<script>
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters(['unfinishedTasks'])
}
}
script>
在上述示例中,我们使用mapGetters将Getter映射为计算属性,可以直接在模板中使用unfinishedTasks。
总结: Getter是Vuex中用于计算和过滤状态的一种机制,类似于组件中的计算属性。通过定义Getter并在组件中使用,我们可以方便地获取和重用派生状态,提高代码的可读性和可维护性。使用Getter可以有效地管理和操作Store中的状态数据。
Action是Vuex中用于处理异步操作的对象,它可以包含任意的异步操作、调用API请求、提交Mutation等。Action提供了一种将逻辑和异步操作从组件中分离的方式,使得代码更加清晰、可维护,并且方便进行单元测试。
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++
}
},
actions: {
incrementAsync(context) {
setTimeout(() => {
context.commit('increment')
}, 1000)
}
}
})
在上述示例中,我们定义了一个名为incrementAsync的Action,它会在1秒后调用commit方法来触发incrementMutation。
<template>
<div>
<p>Count: {{ count }}p>
<button @click="incrementAsync">Increment Asyncbutton>
div>
template>
<script>
export default {
computed: {
count() {
return this.$store.state.count
}
},
methods: {
incrementAsync() {
this.$store.dispatch('incrementAsync')
}
}
}
script>
在上述示例中,我们在组件的方法中使用this.$store.dispatch来触发incrementAsyncAction。
<template>
<div>
<p>Count: {{ count }}p>
<button @click="incrementAsync">Increment Asyncbutton>
div>
template>
<script>
import { mapActions } from 'vuex'
export default {
computed: {
count() {
return this.$store.state.count
},
},
methods: {
...mapActions(['incrementAsync'])
}
}
script>
在上述示例中,我们使用mapActions将Action映射为组件的方法,可以直接在模板中使用incrementAsync方法。
总而言之,Action提供了一种组织和处理异步操作的方式,使得代码更加清晰、可维护,并且能够更好地管理应用状态的变化。它是Vuex中重要的概念之一,帮助我们构建复杂的应用程序。
Module是Vuex中用于组织和管理状态的模块化机制。它允许我们将大型的Vuex应用程序拆分为更小、可维护的模块,每个模块都具有自己的状态、mutations、actions、getters等。
通过使用Module,我们可以将相关的状态和逻辑组合在一起,提高代码的可读性、可维护性,并支持团队协作开发。
const store = new Vuex.Store({
modules: {
cart: {
state: {
items: []
},
mutations: {
addItem(state, item) {
state.items.push(item)
}
},
actions: {
addToCart(context, item) {
context.commit('addItem', item)
}
},
getters: {
cartItemsCount(state) {
return state.items.length
}
}
}
}
})
在上述示例中,我们定义了一个名为cart的Module,它包含了items状态、addItemMutation、addToCartAction和cartItemsCountGetter。
<template>
<div>
<p>Cart Items: {{ cartItemsCount }}p>
<button @click="addToCart">Add to Cartbutton>
div>
template>
<script>
export default {
computed: {
cartItemsCount() {
return this.$store.state.cart.items.length
}
},
methods: {
addToCart() {
this.$store.dispatch('cart/addToCart', { name: 'Product 1', price: 10 })
}
}
}
script>
在上述示例中,我们在组件中使用
this.$store.state.cart.items
来访问cart模块的状态,并使用this.$store.dispatch(‘cart/addToCart’) 来触发addToCartAction。
<template>
<div>
<p>Cart Items: {{ cartItemsCount }}p>
<button @click="addToCart">Add to Cartbutton>
div>
template>
<script>
import { mapState, mapActions } from 'vuex'
export default {
computed: {
...mapState('cart', ['cartItemsCount'])
},
methods: {
...mapActions('cart', ['addToCart'])
}
}
script>
在上述示例中,我们使用mapState将cartItemsCount映射为组件的计算属性,并使用mapActions将addToCart映射为组件的方法。
Vuex 允许我们将 store 分割成模块(module) 。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
const store = createStore({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象。
const moduleA = {
state: () => ({
count: 0
}),
mutations: {
increment (state) {
// 这里的 `state` 对象是模块的局部状态
state.count++
}
},
getters: {
doubleCount (state) {
return state.count * 2
}
}
}
同样,对于模块内部的 action,局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState:
const moduleA = {
// ...
actions: {
incrementIfOddOnRootSum ({ state, commit, rootState }) {
if ((state.count + rootState.count) % 2 === 1) {
commit('increment')
}
}
}
}
对于模块内部的 getter,根节点状态会作为第三个参数暴露出来:
const moduleA = {
// ...
getters: {
sumWithRootCount (state, getters, rootState) {
return state.count + rootState.count
}
}
}
通过使用Module,我们可以将应用状态拆分为更小的模块,使得代码结构更清晰、可维护。每个Module都可以独立开发和测试,且可以嵌套使用,形成层次化的状态管理结构。这对于大型应用程序和团队协作开发非常有益。
在本示例中,我们将创建两个组件:Counter.vue和Button.vue。Counter组件用于显示计数值,Button组件用于触发计数器的增加。
<template>
<div>
<p>Count: {{ count }}p>
div>
template>
<script>
import { mapState } from 'vuex'
export default {
computed: {
...mapState(['count'])
}
}
script>
<template>
<div>
<button @click="increment">Incrementbutton>
div>
template>
<script>
import { mapActions } from 'vuex'
export default {
methods: {
...mapActions(['increment'])
}
}
script>
创建根组件:
在App.vue中,使用Counter和Button组件,并通过Vuex共享状态。
<template>
<div>
<h1>Counter Apph1>
<Counter />
<Button />
div>
template>
<script>
import Counter from './Counter.vue'
import Button from './Button.vue'
export default {
components: {
Counter,
Button
}
}
script>
在这个示例中,Counter组件通过计算属性使用mapState
辅助函数将Vuex的状态映射为组件的数据。Button组件通过方法使用mapActions
辅助函数将Vuex的动作映射为组件的方法。
这样,当我们在浏览器中访问应用时,将会看到一个计数器和一个按钮。点击按钮会增加计数值,并实时更新到Counter组件上。