Vue基础教程(205)网上购物商城开发实战之系统功能模块设计与实现中的商品模块:Vue购物车爱情故事:商品模块如何让你的代码和钱包一起“瘦身”?

一、 为什么商品模块是商场的“C位顶流”?

想象一下,你走进一家超市,发现货架空空如也,收银台却排长队——是不是瞬间想掉头走人?商品模块就是网上商城的“货架陈列师”,它决定了用户第一眼看到什么、能买到什么、以及最终是否愿意掏腰包。

作为一个Vue新手,我曾天真地以为商品模块就是“展示图片和价格”。直到自己动手开发时,才被现实狠狠教育:

  • 商品数据像渣男的心,说变就变(库存实时更新)
  • 用户筛选时比皇帝选妃还挑剔(多条件过滤)
  • 购物车总在“自作主张”玩独立(状态不同步)

不过别慌,今天我们就用一场“代码真人秀”,把商品模块的底裤扒个精光!


二、 商品模块设计:像搭乐高一样简单

1. 数据结构:给商品拍“身份证证件照”

一个合格的商品对象,应该像相亲简历一样信息完整:

商品{
  姓名: "iPhone 15 Pro Max",
  头像: "iphone.jpg",
  身高: 8999, //身价
  体重: "256GB", //规格
  家庭地址: "手机数码", //分类
  是否单身: true //库存状态
}

对应到代码中:

// goodsList.js
export default [
  {
    id: 1, // 唯一ID - 商品的身份证号
    name: "螺蛳粉奶茶",
    price: 28,
    category: "暗黑料理",
    image: "luosifun_tea.jpg",
    stock: 50, // 库存
    isHot: true // 是否热销
  },
  {
    id: 2,
    name: "程序员防脱发套装",
    price: 299,
    category: "救命用品", 
    image: "programmer_kit.jpg",
    stock: 0, // 库存为0=已售罄
    isHot: false
  }
]
2. 组件规划:分清楚谁管什么事
  • 商品列表组件 (GoodsList.vue):负责摆摊,把商品一个个摆出来
  • 商品卡片组件 (GoodsCard.vue):负责吆喝,展示单个商品颜值
  • 分类筛选组件 (CategoryFilter.vue):负责分类,帮用户快速找对象

就像学校食堂打饭:阿姨给你看菜单(GoodsList)、每个菜有个标签(GoodsCard)、旁边还有川湘粤菜分类(CategoryFilter)。


三、 核心功能实现:手把手教你写“心动的信号”

1. 商品列表渲染:让数据“开口说话”
<!-- GoodsList.vue -->
<template>
  <div class="goods-list">
    <!-- v-if和v-for的孽缘:永远别在同一元素使用 -->
    <div v-if="loading">正在玩命加载中...</div>
    <div v-else>
      <goods-card 
        v-for="goods in filteredGoods" 
        :key="goods.id" 
        :goods="goods"
        @add-to-cart="handleAddCart"
      />
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      goodsList: [], // 商品数据仓库
      loading: true
    }
  },
  computed: {
    // 计算属性:Vue的智能筛选小助手
    filteredGoods() {
      return this.goodsList.filter(item => 
        item.stock > 0 // 只显示有库存的商品
      )
    }
  },
  async mounted() {
    // 模拟异步加载数据
    this.goodsList = await this.$api.getGoodsList()
    this.loading = false
  }
}
</script>

避坑指南

  • v-for 必须配 :key,就像戴口罩必须遮鼻子——不然Vue会生气
  • 计算属性会自动缓存,比方法调用性能更好,适合复杂数据处理
2. 商品筛选:帮用户快速找到“真命天子”
<!-- CategoryFilter.vue -->
<template>
  <div class="filter-bar">
    <button 
      v-for="cate in categories" 
      :key="cate"
      :class="{ active: currentCate === cate }"
      @click="changeCategory(cate)"
    >
      {{ cate }}
    </button>
    
    <!-- 价格排序:从贵到穷 or 从穷到贵 -->
    <select v-model="sortType">
      <option value="default">默认排序</option>
      <option value="priceDesc">价格从高到低</option>
      <option value="priceAsc">价格从低到高</option>
    </select>
  </div>
</template>
<script>
export default {
  props: ['categories'],
  data() {
    return {
      currentCate: '全部',
      sortType: 'default'
    }
  },
  watch: {
    // 监听器:当排序方式改变时通知父组件
    sortType(newVal) {
      this.$emit('sort-change', newVal)
    }
  }
}
</script>

在父组件中整合筛选逻辑:

// GoodsList.vue 新增计算属性
computed: {
  filteredGoods() {
    let list = [...this.goodsList]
    
    // 分类筛选
    if (this.currentCategory !== '全部') {
      list = list.filter(item => item.category === this.currentCategory)  
    }
    
    // 价格排序
    if (this.sortType === 'priceDesc') {
      list.sort((a, b) => b.price - a.price)
    } else if (this.sortType === 'priceAsc') {
      list.sort((a, b) => a.price - b.price)
    }
    
    return list
  }
}
3. 购物车联动:让商品“私奔”到购物车
<!-- GoodsCard.vue -->
<template>
  <div class="goods-card">
    <img :src="goods.image" :alt="goods.name">
    <h3>{{ goods.name }}</h3>
    <p class="price">¥{{ goods.price }}</p>
    
    <!-- 库存为0时显示售罄 -->
    <button 
      v-if="goods.stock > 0" 
      @click="addToCart"
      :disabled="isAdding"
    >
      {{ isAdding ? '添加中...' : '加入购物车' }}
    </button>
    <button v-else disabled class="sold-out">已售罄</button>
  </div>
</template>
<script>
export default {
  props: ['goods'],
  data() {
    return {
      isAdding: false
    }
  },
  methods: {
    async addToCart() {
      this.isAdding = true
      // 通知父组件添加商品到购物车
      this.$emit('add-to-cart', this.goods.id)
      
      // 模拟网络请求
      await new Promise(resolve => setTimeout(resolve, 500))
      this.isAdding = false
    }
  }
}
</script>

购物车数据管理(Vuex方案):

// store/index.js
export default new Vuex.Store({
  state: {
    cartList: [] // 购物车商品
  },
  mutations: {
    // 添加商品到购物车
    ADD_TO_CART(state, goods) {
      const existing = state.cartList.find(item => item.id === goods.id)
      if (existing) {
        existing.quantity++ // 已存在则数量+1
      } else {
        state.cartList.push({ ...goods, quantity: 1 })
      }
      
      // 本地存储备份(防止刷新丢失)
      localStorage.setItem('cart', JSON.stringify(state.cartList))
    }
  }
})

四、 性能优化:让你的商城“健步如飞”

1. 图片懒加载:别一次性全搬出来
<template>
  <img v-lazy="goods.image" :alt="goods.name">
</template>
<script>
// 注册Vue懒加载指令
Vue.directive('lazy', {
  inserted: (el, binding) => {
    const observer = new IntersectionObserver((entries) => {
      if (entries[0].isIntersecting) {
        el.src = binding.value
        observer.unobserve(el)
      }
    })
    observer.observe(el)
  }
})
</script>
2. 防抖搜索:别让用户每输一个字就搜索一次
methods: {
  // 防抖函数:等你打完字再搜索
  searchGoods: _.debounce(function(keyword) {
    this.$api.searchGoods(keyword)
  }, 500)
}

五、 完整示例:一个能跑起来的迷你商城

由于篇幅限制,这里给出核心代码架构,完整代码可访问[GitHub仓库]:

<!-- App.vue -->
<template>
  <div id="app">
    <category-filter 
      :categories="categories"
      @category-change="handleCategoryChange"
      @sort-change="handleSortChange"
    />
    
    <goods-list 
      :goods-list="filteredGoods"
      @add-to-cart="handleAddCart"
    />
    
    <!-- 购物车小图标,显示数量 -->
    <div class="cart-icon">
      🛒
      <span class="cart-count">{{ cartCount }}</span>
    </div>
  </div>
</template>
<script>
import GoodsList from './components/GoodsList.vue'
import CategoryFilter from './components/CategoryFilter.vue'

export default {
  components: { GoodsList, CategoryFilter },
  computed: {
    cartCount() {
      return this.$store.state.cartList.reduce((sum, item) => sum + item.quantity, 0)
    }
  }
}
</script>

六、 总结:商品模块开发心法

开发商品模块就像经营一段感情:

  • 数据结构是基础(了解对方的底细)
  • 组件通信是关键(保持有效沟通)
  • 状态管理是保障(记住重要纪念日)
  • 性能优化是长久之道(持续为感情保鲜)

记住,最好的代码不是最复杂的,而是最容易维护的。当你写完商品模块后,不妨问问自己:如果半年后回头看,还能看懂吗?如果新同事接手,能快速理解吗?

彩蛋:实际开发中,你还会遇到用户说“我要五彩斑斓的黑”、“商品图片加载不出来怎么办”等问题——这些我们下期再聊!现在,先去写代码吧,毕竟看十遍不如写一遍✍️

(注:本文代码适用于Vue 2.x版本,Vue 3版本需调整组合式API写法)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

值引力

持续创作,多谢支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值