一、 为什么商品模块是商场的“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写法)

被折叠的 条评论
为什么被折叠?



