You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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: {},
});

错误原因分析

  1. 模板结构错误:增减、删除按钮放在了v-for循环外部,点击时Product变量未定义,直接触发读取id的错误。
  2. count2方法逻辑错误:this.$store.state.cart.Product.id是错误写法,cart是数组,不存在Product属性,应该使用传入的Product参数的id查找。
  3. 直接修改Vuex状态:代码直接操作$store.state.cart,违反Vuex单向数据流规范,状态修改必须通过mutations完成。
  4. 商品结构混淆:直接推入原商品对象,未区分库存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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 00:25:23