Vue3+TypeScript+Pinia:电商购物车实战指南

Vue3 + TypeScript + Pinia构建电商购物车实战

引言

在电商网站中,购物车是连接商品与订单的关键环节。一个功能完善、体验流畅的购物车不仅能提升用户转化率,还能增强用户粘性。本文将带你使用Vue3、TypeScript和Pinia这三个现代前端技术栈,从零开始构建一个电商购物车系统,涵盖商品添加、数量调整、价格计算、库存检查等核心功能。

一、技术栈选型与优势

选择Vue3、TypeScript和Pinia作为开发组合,是基于它们各自的优势:

  • Vue3:采用Composition API,逻辑复用更灵活,性能更优,对TypeScript的支持也更加友好。
  • TypeScript:提供静态类型检查,减少运行时错误,让代码更易维护,特别适合中大型项目。
  • Pinia:Vue官方推荐的状态管理库,比Vuex更轻量,API更简洁,完美支持TypeScript。

二、项目初始化与基础配置

首先使用Vite创建Vue3项目,并配置TypeScript:

npm create vite@latest shopping-cart -- --template vue-ts
cd shopping-cart
npm install

安装Pinia:

npm install pinia

在main.ts中初始化Pinia:

import { createApp } from \'vue\'
import { createPinia } from \'pinia\'
import App from \'./App.vue\'

const app = createApp(App)
app.use(createPinia())
app.mount(\'#app\')

三、设计购物车数据模型

在TypeScript中,我们需要定义购物车和商品的数据类型。创建types/cart.ts:

interface Product {
  id: number
  name: string
  price: number
  stock: number
  image: string
}

interface CartItem {
  product: Product
  quantity: number
}

interface CartState {
  items: CartItem[]
  total: number
  itemCount: number
}

四、创建Pinia购物车Store

创建stores/cart.ts,实现购物车的核心逻辑:

import { defineStore } from \'pinia\'
import type { CartItem, Product, CartState } from \'../types/cart\'

export const useCartStore = defineStore(\'cart\', {
  state: (): CartState => ({
    items: [],
    total: 0,
    itemCount: 0
  }),

  getters: {
    // 计算总价
    getTotal: (state) => {
      return state.items.reduce((sum, item) => sum + item.product.price * item.quantity, 0)
    },

    // 计算商品总数
    getItemCount: (state) => {
      return state.items.reduce((count, item) => count + item.quantity, 0)
    },

    // 检查商品是否在购物车
    isInCart: (state) => (productId: number) => {
      return state.items.some(item => item.product.id === productId)
    }
  },

  actions: {
    // 添加商品到购物车
    addToCart(product: Product, quantity: number = 1) {
      const existingItem = this.items.find(item => item.product.id === product.id)
      
      if (existingItem) {
        // 检查库存
        if (existingItem.quantity + quantity > product.stock) {
          alert(\'库存不足\')
          return
        }
        existingItem.quantity += quantity
      } else {
        this.items.push({ product, quantity })
      }
      
      this.updateTotals()
    },

    // 从购物车移除商品
    removeFromCart(productId: number) {
      this.items = this.items.filter(item => item.product.id !== productId)
      this.updateTotals()
    },

    // 更新商品数量
    updateQuantity(productId: number, quantity: number) {
      const item = this.items.find(item => item.product.id === productId)
      if (item) {
        if (quantity > item.product.stock) {
          alert(\'库存不足\')
          return
        }
        item.quantity = quantity
        if (quantity <= 0) {
          this.removeFromCart(productId)
        }
        this.updateTotals()
      }
    },

    // 更新总计
    updateTotals() {
      this.total = this.getTotal
      this.itemCount = this.getItemCount
    },

    // 清空购物车
    clearCart() {
      this.items = []
      this.updateTotals()
    }
  }
})

五、构建购物车组件

创建components/CartList.vue:

<template>
  <div class=\"cart-list\">
    <h2>购物车</h2>
    <div v-if=\"cartStore.items.length === 0\">
      购物车为空
    </div>
    <div v-else>
      <div v-for=\"item in cartStore.items\" :key=\"item.product.id\" class=\"cart-item\">
        <img :src=\"item.product.image\" :alt=\"item.product.name\">
        <div class=\"item-info\">
          <h3>{{ item.product.name }}</h3>
          <p>¥{{ item.product.price.toFixed(2) }}</p>
          <div class=\"quantity-control\">
            <button @click=\"updateQuantity(item.product.id, item.quantity - 1)\">-</button>
            <span>{{ item.quantity }}</span>
            <button @click=\"updateQuantity(item.product.id, item.quantity + 1)\">+</button>
          </div>
        </div>
        <button class=\"remove-btn\" @click=\"removeFromCart(item.product.id)\">删除</button>
      </div>
      <div class=\"cart-summary\">
        <p>总计: ¥{{ cartStore.total.toFixed(2) }}</p>
        <button @click=\"clearCart\">清空购物车</button>
      </div>
    </div>
  </div>
</template>

<script setup lang=\"ts\">
import { useCartStore } from \'../stores/cart\'
import type { Product } from \'../types/cart\'

const cartStore = useCartStore()

const updateQuantity = (productId: number, quantity: number) => {
  cartStore.updateQuantity(productId, quantity)
}

const removeFromCart = (productId: number) => {
  cartStore.removeFromCart(productId)
}

const clearCart = () => {
  cartStore.clearCart()
}
</script>

六、商品列表与购物车联动

创建components/ProductList.vue:

<template>
  <div class=\"product-list\">
    <h2>商品列表</h2>
    <div class=\"products\">
      <div v-for=\"product in products\" :key=\"product.id\" class=\"product\">
        <img :src=\"product.image\" :alt=\"product.name\">
        <div class=\"product-info\">
          <h3>{{ product.name }}</h3>
          <p>¥{{ product.price.toFixed(2) }}</p>
          <p>库存: {{ product.stock }}</p>
          <button 
            @click=\"addToCart(product)\" 
            :disabled=\"cartStore.isInCart(product.id)\"
          >
            {{ cartStore.isInCart(product.id) ? \'已在购物车\' : \'加入购物车\' }}
          </button>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang=\"ts\">
import { ref } from \'vue\'
import { useCartStore } from \'../stores/cart\'
import type { Product } from \'../types/cart\'

const cartStore = useCartStore()

// 模拟商品数据
const products = ref<Product[]>([
  { id: 1, name: \'商品A\', price: 99.99, stock: 10, image: \'/product-a.jpg\' },
  { id: 2, name: \'商品B\', price: 199.99, stock: 5, image: \'/product-b.jpg\' },
  { id: 3, name: \'商品C\', price: 299.99, stock: 8, image: \'/product-c.jpg\' }
])

const addToCart = (product: Product) => {
  cartStore.addToCart(product)
}
</script>

七、优化与扩展功能

1. **持久化存储**:使用localStorage保存购物车状态,实现刷新不丢失:

// 在stores/cart.ts中添加
import { persist } from \'pinia-plugin-persistedstate\'

export const cartStore = defineStore(\'cart\', {
  // ...原有配置
}, {
  persist: {
    key: \'shopping-cart\',
    storage: localStorage,
    paths: [\'items\']
  }
})

// 在main.ts中注册插件
import { createPinia } from \'pinia\'
const pinia = createPinia()
pinia.use(persist)

2. **购物车动画**:使用Vue Transition实现添加/删除商品的动画效果。

3. **商品推荐**:基于购物车商品推荐相关商品。

4. **优惠计算**:添加优惠券、满减等促销逻辑。

八、总结

通过本文的实践,我们完成了基于Vue3、TypeScript和Pinia的电商购物车系统开发。这个购物车不仅实现了基本的商品管理功能,还考虑了库存检查、数据持久化等实际应用场景。Composition API的灵活使用让代码逻辑更加清晰,TypeScript的类型安全则减少了潜在的运行时错误。Pinia作为状态管理库,提供了简洁的API和优秀的TypeScript支持,让状态管理变得轻松愉快。

在实际项目中,还可以进一步扩展购物车的功能,比如添加商品规格选择、运费计算、多收货地址支持等。但无论功能如何扩展,本文提供的核心架构和实现思路都能作为良好的基础。希望这个实战案例能帮助你更好地理解Vue3生态系统的协同工作方式,并在实际项目中应用这些技术。

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
none
暂无评论...