如何在 SPA 应用中使用 Vuex 管理应用状态
随着大前端时代的到来,单页应用(SPA)已经成为了最为流行的开发模式之一,而 Vuex 是一个专门为 Vue.js 应用开发的状态管理模式,它能够非常便捷地解决应用中组件间通讯、数据传递等问题。本文将详细介绍如何在SPA应用中使用 Vuex 管理应用状态。
何为 Vuex
Vuex 是一个专为 Vue.js 应用开发的状态管理模式,它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以可预测的方式发生变化。它可以让我们对组件的状态管理变得更加方便和简单,提高代码重用性和维护性。
Vuex 的基本概念:
- state:驱动应用的数据源,存储应用级别的状态。
- getter:获取 state 中的数据。
- mutation:修改 state 中的数据,确保所有修改都通过 mutation,而非直接修改 state。
- action:提交 mutation,而不是直接变更状态,可以包含任意异步操作。
安装使用
安装 Vuex 并初始化一个 Store:
// www.javascriptcn.com code example
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");
},
},
});
new Vue({
el: "#app",
store,
});在组件中使用:
// www.javascriptcn.com code example
<template>
<div>{{ count }}</div>
<button @click="increment">Increment</button>
</template>
<script>
export default {
computed: {
count() {
return this.$store.state.count;
},
},
methods: {
increment() {
this.$store.dispatch("increment");
},
},
};
</script>上面的代码中,我们创建了一个 Store,并定义了一个 state(count)和一个 mutation(increment)。然后,我们将 Store 注入到 Vue 实例中,这样在所有的组件中都可以使用 this.$store 访问该 Store 实例。在这个例子中,我们在组件中使用了 count 属性和 increment 方法。
在使用 Vuex 管理状态时,它的重要性在于 state 能够被可视化地追踪和调试,这是具备良好维护性的前提。
Vuex 的进阶
下面我们通过一个具体的例子来了解如何在SPA应用中使用 Vuex 管理应用状态,同时探讨下 Vuex 的进阶应用。
例子
假设我们正在开发一个简单的电商网站,它包括商品展示页面、购物车页面和结算页面。首先,我们需要设计应用的状态数据:
// www.javascriptcn.com code example
state: {
products: [
{ id: 1, title: 'Apple', price: 0.5, stock: 10 },
{ id: 2, title: 'Banana', price: 0.2, stock: 10 },
{ id: 3, title: 'Cherry', price: 0.8, stock: 10 },
{ id: 4, title: 'Durian', price: 2.5, stock: 10 },
],
cart: []
}其中,products 是商品列表,cart 是购物车,它们是应用的两个核心状态数据。
接下来,我们需要设计一些 mutation 来修改这些状态数据:
// www.javascriptcn.com code example
mutations: {
addToCart(state, productId) {
const product = state.products.find(p => p.id === productId)
if (product.stock > 0) {
const item = state.cart.find(i => i.productId === productId)
if (item) {
item.quantity++
} else {
state.cart.push({ productId, quantity: 1 })
}
product.stock--
}
},
removeFromCart(state, productId) {
const item = state.cart.find(i => i.productId === productId)
if (item) {
const product = state.products.find(p => p.id === productId)
if (product) {
product.stock += item.quantity
}
state.cart.splice(state.cart.indexOf(item), 1)
}
}
}上面的代码中,addToCart 用于添加商品到购物车,removeFromCart 用于将商品从购物车中删除。
接下来,我们需要设计一些 getter 来获取这些状态数据:
// www.javascriptcn.com code example
getters: {
cartProducts(state) {
return state.cart.map(item => {
const product = state.products.find(p => p.id === item.productId)
return {
title: product.title,
price: product.price,
quantity: item.quantity
}
})
},
cartTotalPrice(state, getters) {
return getters.cartProducts.reduce((total, product) => {
return total + product.price * product.quantity
}, 0)
}
}上面的代码中,cartProducts 是获取购物车商品列表,cartTotalPrice 是获取购物车总价格。
最后,我们需要设计一些 action 来调用 mutation 实现异步操作:
// www.javascriptcn.com code example
actions: {
checkout({ state }) {
return new Promise(resolve => {
setTimeout(() => {
state.cart.forEach(item => {
const product = state.products.find(p => p.id === item.productId)
product.stock -= item.quantity
})
resolve()
}, 1000)
})
}
}上面的代码中,checkout 是对购物车中的商品进行结算,它会减少商品库存数量。
完整代码
上面的代码只是应用状态数据的极为简单的设计,实际中应用的状态数据更为丰富,有时还需要组合计算等处理。完整代码如下:
// www.javascriptcn.com code example
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
products: [
{ id: 1, title: 'Apple', price: 0.5, stock: 10 },
{ id: 2, title: 'Banana', price: 0.2, stock: 10 },
{ id: 3, title: 'Cherry', price: 0.8, stock: 10 },
{ id: 4, title: 'Durian', price: 2.5, stock: 10 },
],
cart: []
},
mutations: {
addToCart(state, productId) {
const product = state.products.find(p => p.id === productId)
if (product.stock > 0) {
const item = state.cart.find(i => i.productId === productId)
if (item) {
item.quantity++
} else {
state.cart.push({ productId, quantity: 1 })
}
product.stock--
}
},
removeFromCart(state, productId) {
const item = state.cart.find(i => i.productId === productId)
if (item) {
const product = state.products.find(p => p.id === productId)
if (product) {
product.stock += item.quantity
}
state.cart.splice(state.cart.indexOf(item), 1)
}
}
},
getters: {
productById(state) {
return (id) => {
return state.products.find(product => product.id === id)
}
},
cartProducts(state) {
return state.cart.map(item => {
const product = state.products.find(p => p.id === item.productId)
return {
title: product.title,
price: product.price,
quantity: item.quantity
}
})
},
cartTotalPrice(state, getters) {
return getters.cartProducts.reduce((total, product) => {
return total + product.price * product.quantity
}, 0)
}
},
actions: {
checkout({ state }) {
return new Promise(resolve => {
setTimeout(() => {
state.cart.forEach(item => {
const product = state.products.find(p => p.id === item.productId)
product.stock -= item.quantity
})
resolve()
}, 1000)
})
}
}
});
new Vue({
el: '#app',
store,
computed: {
cartProducts() {
return this.$store.getters.cartProducts
},
cartTotalPrice() {
return this.$store.getters.cartTotalPrice.toFixed(2)
},
productById() {
return this.$store.getters.productById
}
},
methods: {
addToCart(id) {
this.$store.commit('addToCart', id)
},
removeFromCart(id) {
this.$store.commit('removeFromCart', id)
},
checkout() {
this.$store.dispatch('checkout').then(() => {
alert('结算完成!')
})
}
}
});结语
使用 Vuex 管理应用状态能够较好地解决应用中状态分散、状态传递困难等问题,提高应用的重用性和维护性。需要注意的是,在实际应用过程中,我们需要灵活运用 Vuex 中提供的状态管理策略,具体采取何种策略,取决于应用的具体情况和需求。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67cfd5d7e46428fe9ebf4bfb