跳转到内容

购物车

刀刀

1/2/2025

0 字

0 分钟

思路

购物车模块分为未登录本地购物车和登录状态接口获取的购物车。

未登录状态下,数据将保存到本地存储中,在本地做增删改查操作。

登录状态下,数据通过接口动态获取,增删改查操作都调用接口向服务器发起请求。

在未登录状态下增加的数据登录后会调用接口合并购物车数据。

未登录状态

未登录状态下做的增删改查操作都是直接修改本地的数据,因此只需要通过数组内置的 map()filter()find() 等方法实现即可。

由于购物车数据多个页面都需要使用,因此放在 pinia 内做状态管理存储是不错的选择。代码如下所示:

js
import { ref } from 'vue'
import { defineStore } from 'pinia'

export const useCarttStore = defineStore('cart', () => {
  const cartList = ref([]) // 购物车列表数据

  // 购物车商品总数数据计算
  const cartCount = computed(() => cartList.value.length ? cartList.value.reduce((pre, next) => pre + next.count, 0) : 0)
 
  return { cartList, cartCount }
}, {
  persist: true
})

通过 pinia 插件设置 persist 实现本地存储,刷新后数据依旧存在。

piniastate 状态数据通过 ref 创建,getter 计算数据则通过 computed 计算属性获取。最后通过 return 导出,.vue 组件按需导入使用,代码如下:

js
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'

export const useCarttStore = defineStore('cart', () => {
  const cartList = ref([]) // 购物车列表数据

  return { cartList }
}, {
  persist: true
})

通过 Vue Devtools 插件查看效果,如下图所示:

pCVTIts.png

增加

购物车增加数据逻辑步骤为:

  1. 获取新增的商品数据,如数量、唯一标识、价钱、skuId
  2. 判断数组内是否有该商品,没有则往数组内追加,存在则数量累加
js
// 添加购物车操作
const addCart = async (e) => {
    // 无token,本地操作
    let obj = {
      id: e.id,
      name: e.name,
      price: e.price,
      count: e.count,
      skuId: e.skuId,
      picture: e.mainPictures[0],
      attrsText: e.specsText,
      selected: true
    }

    // 判断是否添加过:添加过:数量加一;没添加过,添加数据
    const item = cartList.value.find(v => v.skuId === obj.skuId)
    if (item) {
      item.count += obj.count
    } else {
      cartList.value.push(obj)
    }
}

删除

通过 filter 方法过滤掉选择的商品,把新数组赋值给 cartList 数组,获取过滤后的数据,即实现删除操作。

js
// 删除购物车内容
const delCart = async skuId => {
    cartList.value = cartList.value.filter(item => item.skuId !== skuId)
}

修改

通过数组的 find 方法,找到选中的该项数据的 item 子选项,修改它的属性。由于 Vue3 通过代理侦听作为响应式,因此该改动也能被侦听到。

js
// 修改购物车的选中状态
const updateCheck = (e, i) => {
  const item = cartList.value.find(item => item.skuId === i.skuId)
  item.selected = e
}

登录状态

登录后后端接口会返回用户的 token ,而前面为了方便多页面使用 token 在实现登录功能的时候已经把数据保存到 pinia 中,因此这里只需要引入,判断是否存在 token

如果有 token ,说明已经登录了,调用接口实现功能即可。

在未登录状态下保存的数据登录后需要调用接口合并购物车,否则未登录状态下保存的数据将无意义。

退出登录后要把购物车数据清空,登录后重新调接口获取最新的数据。

点击查看代码
js
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
import { useUserStore } from './user'
import { storeToRefs } from "pinia";
import { insertCartAPI, getCartListAPI, removeCartItemAPI } from '@api/cart'

export const useCarttStore = defineStore('cart', () => {
  const { userinfo } = storeToRefs(useUserStore())
  const cartList = ref([]) // 购物车列表数据

  // 计算token
  const hasToken = computed(() => userinfo.value.token)

  // 获取最新购物车数据
  const getCartListFn = async () => {
    const res = await getCartListAPI()
    cartList.value = res.result
  }

  // 添加购物车操作
  const addCart = async (e) => {
    if (hasToken) {
      // 有token,调用接口
      const { skuId, count } = e
      await insertCartAPI({ skuId, count })
      getCartListFn()
    } else {
      // ...
    }
  }

  // 购物车商品总数数据计算
  const cartCount = computed(() => cartList.value.length ? cartList.value.reduce((pre, next) => pre + next.count, 0) : 0)
  // ...

  // 删除购物车内容
  const delCart = async skuId => {
    if (hasToken) {
      // 登录状态,调用接口删除数据
      await removeCartItemAPI(skuId)
      getCartListFn()
    } else {
      // ...
    }
  }

  // 清空购物车
  const clearCart = () => {
    cartList.value = []
  }

  return { cartList, addCart, delCart, cartCount, ... }
}, {
  persist: true
})

无响应式BUG

在功能实现时,发现有一个情况:

  1. 在做新增或删除操作时,页面数据无更新
  2. 查看本地存储和 Vue Devtools 插件时,发现 pinia 中与本地存储中数据已经是最新的了,而 .vue 组件中数据还是旧数据。说明获取到的数据不是响应式的。

解决方案:通过 pinia 提供的 storeToRef 属性,把获取到的值转为响应式。

注意:该方法不能用于函数使用。