Vue3+Vuex购物车去重:实现数量增减及修复id读取报错
Vue3 + Vuex 电商购物车:重复添加商品报错及数量更新优化
我用Vue3结合Vuex搭建电商网站,当前添加商品到购物车时,重复添加同一商品会生成重复条目,希望改成通过增减按钮更新商品数量的方式,但实现时触发错误:Uncaught TypeError: Cannot read properties of undefined (reading 'id'),错误来自cart.vue的count2方法,相关代码如下:
shop.vue
<template> Cart Items: <cart-badge :count="cartLength">{{ count }}</cart-badge> <div class="shop"> <div class="products" v-for="Product in products" :key="Product.id"> <h1>{{ Product.name }}</h1> <img :src="Product.pic" width="400" /> <br> {{ Product.description }} <br> {{ "$" + Product.price }} <br> <button class="addToCart" @click="addToCart(Product)">Add to Cart</button> </div> </div> </template> <script> import CartBadge from '../components/CartBadge.vue'; export default { name: 'shop', methods: { addToCart(Product) { this.$store.state.cart.push(Product) console.log(this.$store.state.cart) }, }, components: { 'cart-badge': CartBadge }, computed: { cartLength() { return this.$store.state.cart.length; }, products() { return this.$store.state.Product; }, cart() { return this.$store.state.cart; } } } </script>
db.json
[ { "id": 1, "name": "iphone 11", "pic": "/pics/iphone11.jpg", "description": "newest iphone!", "price": 999, "qty": 10 }, { "id": 2, "name": "galaxy s22", "pic": "/pics/galaxy22.jpg", "description": "a new phone from samsung!", "price": 1200, "qty": 10 } ]
cart.vue
<template> <h1>Cart</h1> <div class="cartItems"> Cart Items: <cart-badge :count="cartLength">{{ count }}</cart-badge> <p v-if="!cart.length">Your cart is empty! please add something to the cart</p> <div class="products" v-for="Product in cart" :key="Product.id">name: {{ Product.name }} price: {{ Product.price }}</div> <button v-if="cart.length" class="removeFromCart" @click="removeFromCart(Product)">X</button> <button v-if="cart.length" class="increment" @click="count2(Product)">+</button> <button v-if="cart.length" class="descrement" @click="count--">-</button> <strong v-if="cart.length">Total ${{ cartSum }}</strong> <p>{{ count }}</p> </div> </template> <script> import CartBadge from '../components/CartBadge.vue'; import { mapGetters } from 'vuex'; export default { name: 'Cart', data() { return { count: 0 } }, components: { 'cart-badge': CartBadge }, computed: { cartLength() { return this.$store.state.cart.length; }, products() { return this.$store.state.Product; }, cart() { return this.$store.state.cart; }, ...mapGetters([ "cartSum" ]) }, methods: { removeFromCart(Product) { this.$store.state.cart.splice(Product) console.log(this.$store.state.cart) }, count2(Product) { let FoundProduct = this.$store.state.cart.find(o => o.id === this.$store.state.cart.Product.id) if (FoundProduct) { FoundProduct.qty += 1; return; } this.$store.state.cart.Product.id = Product.id this.$store.state.cart.push(this.$store.state.cart.Product) } } } </script>
store.js(Vuex状态管理文件)
import { createStore } from "vuex"; import Product from "../db.json"; export default createStore({ state: { cart: [], Product, }, getters: { cart: (state) => state.cart, cartSum: (state) => { return state.cart.reduce((total, Product) => { return (total += Product.price * 1); }, 0); }, }, mutations: {}, actions: {}, modules: {}, });
错误原因分析
- 模板结构错误:增减、删除按钮放在了
v-for循环外部,点击时Product变量未定义,直接触发读取id的错误。 - count2方法逻辑错误:
this.$store.state.cart.Product.id是错误写法,cart是数组,不存在Product属性,应该使用传入的Product参数的id查找。 - 直接修改Vuex状态:代码直接操作
$store.state.cart,违反Vuex单向数据流规范,状态修改必须通过mutations完成。 - 商品结构混淆:直接推入原商品对象,未区分库存
qty和购物车独立数量字段,导致无法单独维护购物车数量。
解决方案
1. 重构Vuex Store,添加购物车操作mutations
import { createStore } from "vuex"; import Product from "../db.json"; export default createStore({ state: { cart: [], Product, }, getters: { cart: (state) => state.cart, cartSum: (state) => { return state.cart.reduce((total, item) => { return total += item.price * item.quantity; }, 0); }, }, mutations: { // 添加商品,存在则增数量,不存在则新增 ADD_TO_CART(state, product) { const existingItem = state.cart.find(item => item.id === product.id); if (existingItem) { existingItem.quantity += 1; } else { state.cart.push({ ...product, quantity: 1 // 购物车独立数量字段,和库存qty区分 }); } }, // 增加商品数量 INCREMENT_ITEM(state, productId) { const item = state.cart.find(item => item.id === productId); if (item) item.quantity += 1; }, // 减少商品数量,数量为1时移除 DECREMENT_ITEM(state, productId) { const item = state.cart.find(item => item.id === productId); if (item) { item.quantity > 1 ? item.quantity -= 1 : state.cart = state.cart.filter(i => i.id !== productId); } }, // 移除商品 REMOVE_FROM_CART(state, productId) { state.cart = state.cart.filter(item => item.id !== productId); } }, actions: {}, modules: {}, });
2. 修改shop.vue,通过mutation添加商品
<template> Cart Items: <cart-badge :count="cartLength">{{ count }}</cart-badge> <div class="shop"> <div class="products" v-for="product in products" :key="product.id"> <h1>{{ product.name }}</h1> <img :src="product.pic" width="400" /> <br> {{ product.description }} <br> {{ "$" + product.price }} <br> <button class="addToCart" @click="addToCart(product)">Add to Cart</button> </div> </div> </template> <script> import CartBadge from '../components/CartBadge.vue'; export default { name: 'shop', methods: { addToCart(product) { this.$store.commit('ADD_TO_CART', product); console.log(this.$store.state.cart); }, }, components: { 'cart-badge': CartBadge }, computed: { cartLength() { return this.$store.state.cart.length; }, products() { return this.$store.state.Product; }, cart() { return this.$store.state.cart; } } } </script>
3. 修复cart.vue模板及方法
将操作按钮移入v-for循环内,通过mutation操作状态:
<template> <h1>Cart</h1> <div class="cartItems"> Cart Items: <cart-badge :count="cartLength">{{ count }}</cart-badge> <p v-if="!cart.length">Your cart is empty! please add something to the cart</p> <div class="products" v-for="item in cart" :key="item.id"> name: {{ item.name }} price: {{ item.price }} <span>数量: {{ item.quantity }}</span> <button class="removeFromCart" @click="removeFromCart(item.id)">X</button> <button class="increment" @click="incrementItem(item.id)">+</button> <button class="descrement" @click="decrementItem(item.id)">-</button> </div> <strong v-if="cart.length">Total ${{ cartSum }}</strong> </div> </template> <script> import CartBadge from '../components/CartBadge.vue'; import { mapGetters } from 'vuex'; export default { name: 'Cart', data() { return { count: 0 } }, components: { 'cart-badge': CartBadge }, computed: { cartLength() { return this.$store.state.cart.length; }, products() { return this.$store.state.Product; }, cart() { return this.$store.state.cart; }, ...mapGetters([ "cartSum" ]) }, methods: { removeFromCart(productId) { this.$store.commit('REMOVE_FROM_CART', productId); }, incrementItem(productId) { this.$store.commit('INCREMENT_ITEM', productId); }, decrementItem(productId) { this.$store.commit('DECREMENT_ITEM', productId); } } } </script>
内容的提问来源于stack exchange,提问作者Xanward
相关产品推荐
相关产品推荐

