后端数据模拟
根目录创建 vue.config.js,写入接口和数据,安装 cnpm i axios -S ,重启服务器
module.exports = {
devServer: {
before(app, serve) {
app.get('/api/products', (req, res) => {
res.json({
results: products
})
})
}
}
}
const products = [
{ id: 1, title: 'iphone11', price: 800, inventory: 10 },
{ id: 2, title: 'iphone11 pro', price: 1200, inventory: 15 },
{ id: 3, title: 'iphone11 pro max', price: 1400, inventory: 7 }
]
获取数据 & 渲染数据列表
vuex 产品模块 products.js 内引入axios 并异步发送请求获取数据
import Axiso from 'axios'
export default {
namespaced: true,
state: {
products: []
},
mutations: {
getAllProducts(state, results) {
state.products = results; //3.将获得的数据赋值给当前state内的products
}
},
actions: {
async getAllProducts({ commit }) { //2.发送异步请求获取数据 提交mutation
try {
const res = await Axiso.get('/api/products'); //向接口请求数据
const results = res.data.results; //将获得的数据提取赋值给新的属性
commit('getAllProducts', results) //commit调用mutation并传递获得的数据
} catch (error) {
console.log(error);
}
}
}
}
获取到数据:
data: {…}, status: 200, statusText: "OK", headers: {…}, config: {…}, …}
products.vue组件中:
<template>
<div>
<h2>商品</h2>
<ul><!-- 5.渲染产品列表 -->
<li v-for="product in products" :key="product.id">
<h3>{{product.title}} - {{product.price}}</h3>
</li>
</ul>
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
computed: {
...mapState("products", ["products"]) //4.辅助函数获取store内products模块的状态
},
created() {
this.$store.dispatch("products/getAllProducts");//1.组件创建后调用store内产品组件的Action获取服务器数据
}
};
</script>
fliter修饰数据
main.js 内注册全局过滤器:
Vue.filter('currency', (value) => {
return '$' + value;
})
组件内引用:
<ul>
<li v-for="product in products" :key="product.id">
<h3>{{product.title}} - {{product.price | currency}}</h3>
</li>
</ul>
注册全局组件
main.js 内注册全局组件:
import CartList from '@/components/CartList.vue' //引入组件
Vue.component(CartList.name, CartList) //注册全局组件
export default {
name: "CartList" //组件名 注册全局组件时使用
};
向购物车模块添加数据
产品组件内获取vuex购物车模块内的方法并添加事件按钮触发并传值
<template>
<div>
<h2>产品</h2>
<ul>
<li v-for="product in products" :key="product.id">
<h3>{{product.title}} - {{product.price | currency}} - {{product.inventory}}</h3>
<!-- 2.添加点击事件触发action并传值当前点击的数据对象给store(会包括列表渲染里没有引用的) -->
<button @click="addProductToCart(product)">添加到购物车</button>
</li>
</ul>
</div>
</template>
<script>
import { mapState, mapActions } from "vuex";
export default {
computed: {
...mapState("products", ["products"])
},
created() {
this.$store.dispatch("products/getAllProducts");
},
methods: {
//1.获取vuex购物车模块内的方法
...mapActions("cartList", ["addProductToCart"])
}
};
</script>
购物车模块接收触发事件和传入的数据,进行判断并存储和修改购物车数据
export default {
namespaced: true,
state: {
cartList: [] //3.声明购物车状态
},
getters: {
evenOrOdd2(state) {
return state.count % 2 === 0 ? '偶数' : '奇数'
}
},
mutations: {
//7.添加到购物车产品名称
pushProductToCart(state, { id, quantity }) {
state.cartList.push({
id,
quantity
})
},
//7.产品数量加一
addProductQuantity(state, { id }) {
//根据传入的数据的id,获取cartList对应id数据
const product = state.cartList.find(item => item.id === id);
//对这条数据的数量加一
product.quantity++;
}
},
actions: {
addProductToCart({ commit, state }, product) { //4.调用添加购物车方法传入state,接收product
if (product.inventory > 0) { //5.从传入的product全部数据中的inventory判断库存是否大于0
//获取购物车cartList中的数据,判断item中对应id的数据,和传入的数据,是否一样并赋值一个变量
const cartItem = state.cartList.find(item => item.id === product.id);
console.log(cartItem);
if (!cartItem) { //6.cartList对比传入数据的id为空,没有这条数据,cartItem为faulse
//购物车没有数据 把数据添加到cartList
commit('pushProductToCart', { id: product.id, quantity: 1 })
} else {
//购物车中已有数据 传递这个产品的id到产品添加方法
commit('addProductQuantity', { id: product.id })
}
}
}
}
}
渲染购物车列表
vuex购物车模块内getter方法生成数据
state: {
cartList: [],
},
getters: {
//通过getter的第三个参数获得产品模块的数据和当前购物车模块的cartList数据比对,生成购物车数据
getCartList(state, getters, rootState) {
//通过map将cartList的数据遍历为有序列表并解构出id和quantity,
//返回的是map产生的新数组,map不会对空数组进行检测
return state.cartList.map(({ id, quantity }) => {
/*第一个products是模块名,第二个是状态名,获取产品模块的所有产品数据的一个数组,
通过find方法内的函数判断,map对cartList的每次遍历对产品全部数据进行逐个比对,
获得相同id的对象对product进行赋值*/
const product = rootState.products.products.find(item => item.id === id);
return { //cartList的每一产品id对应的product对象,
//获取产品数据的title和price和cartList.id的数量,返回给map存储到新的数组
title: product.title, //title为product的title
price: product.price, //price为product的title
quantity //数量为当前购物车模块内对应id的数量
}
})
}
}
购物车组件获取getter生成的购物车数据
<template>
<div>
<h2>购物车</h2>
<ul><!-- 将getter数据渲染为列表 -->
<li v-for="(item,index) in getCartList" :key="index">
<h3>{{item.title}} - {{item.price | currency}} × {{item.quantity}}</h3>
</li>
</ul>
</div>
</template>
<script>
import { mapGetters } from "vuex";
export default {
name: "CartList",
computed: {
//获取vuex购物车模块的getter方法生成的购物车数据
...mapGetters("cartList", ["getCartList"])
}
};
</script>
总价格计算
vuex购物车模块内:
getters: {
getCartList(state, getters, rootState) {
return state.cartList.map(({ id, quantity }) => {
const product = rootState.products.products.find(item => item.id === id);
return {
title: product.title,
price: product.price,
quantity
}
})
},
//1.添加购物车合计价格方法,并传入第二个参数getters来获取另一个getter
cartTotalPrice(state, getters) {
//2.获得上方getCartList返回的数组用reduce()方法计算并传入getCartList生成的数组product
return getters.getCartList.reduce((total, product) => {
//3.total作为累加器并从0开始累加product的价格和数量相乘的单个产品的总价,reduce()方法返回相加所有产品的总价
return total + product.price * product.quantity
}, 0);
}
},
reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终相加计算为一个值,因为购物车在没用数据的时候是空值,reduce()方法在传入对象为空时会报错,传入默认值,并从0开始计算
<template>
<div>
<h2>购物车</h2>
<ul>
<li v-for="(item,index) in getCartList" :key="index">
<h3>{{item.title}} - {{item.price | currency}} × {{item.quantity}}</h3>
</li>
</ul>
<!-- 5.引用方法并添加全局过滤器 -->
<h3>总价格: {{cartTotalPrice | currency}}</h3>
</div>
</template>
<script>
import { mapGetters } from "vuex";
export default {
name: "CartList",
computed: {
//4.辅助函数内添加vuex购物车模块总价计算的方法
...mapGetters("cartList", ["getCartList", "cartTotalPrice"])
}
};
</script>
减库存
在购物车模块内的添加产品到购物车方法下,提交对产品模块内方法的调用
actions: {
addProductToCart({ commit, state }, product) {
if (product.inventory > 0) {
const cartItem = state.cartList.find(item => item.id === product.id);
if (!cartItem) {
commit('pushProductToCart', { id: product.id, quantity: 1 })
} else {
commit('addProductQuantity', { id: product.id })
}
//每次点击加入购物车,触发products模块内的这个方法,并传值当前点击product的id
//如果想在一个模块中提交另一个模块中的方法,那么需要第三个参数 { root:true } 来返回到根
commit('products/decrementProductsInventory', { id: product.id }, { root: true })
}
}
}
产品模块的mutations内:
mutations: {
//方法传入产品数据,并解构出传入产品的id
decrementProductsInventory(state, { id }) {
//模块内的数据比对传入的id,获得对应的产品数据
const product = state.products.find(item => item.id === id);
//每次触发产品的库存减一
product.inventory--;
}
}
库存为0时按钮不可点击
通过绑定button的 disabled 事件判断当前产品的库存是否为0,为0时为faulse,取反为true,让 disabled 生效
<button :disabled="!product.inventory" @click="addProductToCart(product)">添加到购物车</button>