阶段 2 – 购物车(超详细版)
目标
- 把“加入购物车”做成 全局状态,任何页面都能读写
- 在本地 持久化(关闭小程序后购物车仍在)
- 新建 购物车页:数量增减、总价实时计算、去结算入口
- 打 Git Tag
v2.0‑cart
1. 学到的核心技术
技术/概念 | 关键 API/组件 | 为什么要学 |
---|---|---|
全局状态 | App.globalData / 简易 store.js |
小项目先不引入第三方状态库,足够用 |
本地持久化 | wx.setStorageSync / wx.getStorageSync |
保证用户切后台、重进小程序后数据不丢 |
UI 组件 | (弹出购物车) (底部结算条) (数量加减) |
快速完成电商式交互 |
TabBar Badge | wx.setTabBarBadge |
在底部“购物车”图标上显示件数 |
2. 项目结构新增
miniprogram/
├─ store/ # 新增
│ └─ cart.js
├─ pages/
│ ├─ index/ # 首页已存在
│ └─ cart/ # 新建购物车页面
3. 编写轻量级全局 Store
路径:
miniprogram/store/cart.js
const CART_KEY = 'CART_V1'
const store = {
data: { items: {} },
load() {
this.data.items = wx.getStorageSync(CART_KEY) || {}
},
save() {
wx.setStorageSync(CART_KEY, this.data.items)
},
add(dish) {
const { _id } = dish
if (this.data.items[_id]) {
this.data.items[_id].count += 1
} else {
this.data.items[_id] = { ...dish, count: 1 }
}
this.save()
},
totalCount() {
return Object.values(this.data.items).reduce((s, i) => s + i.count, 0)
},
totalPrice() {
return Object.values(this.data.items)
.reduce((s, i) => s + i.count * i.price, 0)
.toFixed(2)
}
}
module.exports = store
4. 在 app.js
中加载购物车
// miniprogram/app.js
App({
onLaunch() {
const cart = require('./store/cart')
cart.load()
this.globalData = { cart }
}
})
5. 修改首页:加入购物车 & 更新角标
文件:
pages/index/index.js
只需要把之前的 onAddCart
改成:
import cart from '../../store/cart'
onAddCart(e) {
const dish = e.currentTarget.dataset.dish
cart.add(dish)
wx.setTabBarBadge({ index: 1, text: String(cart.totalCount()) })
wx.showToast({ title: '已加入购物车', icon: 'success' })
}
}
提示:在
app.json
的tabBar
数组里,把第二项页面路径设成"pages/cart/index"
,这样角标才会显示在购物车图标上。
6. 新建购物车页面
6.1 组件声明 pages/cart/index.json
{
"navigationBarTitleText": "购物车"
}
6.2 页面布局 index.wxml
<view class="page">
<block wx:for="{{list}}" wx:key="_id">
<view class="cart-item">
<image class="thumb" src="{{item.img}}" mode="aspectFill" />
<view class="info">
<text class="name">{{item.name}}</text>
<text class="price">¥{{item.price}}</text>
<text class="count">x {{item.count}}</text>
</view>
</view>
</block>
<view class="bottom-bar">
<text>共 {{totalCount}} 件</text>
<text>合计:¥{{totalPrice}}</text>
<button type="primary" bindtap="onCheckout">去结算</button>
</view>
</view>
6.3 页面逻辑 index.js
const cart = require('../../store/cart')
Page({
data: {
list: [],
totalCount: 0,
totalPrice: '0.00'
},
onShow() {
cart.load()
this.refresh()
},
refresh() {
const items = Object.values(cart.data.items)
const totalCount = cart.totalCount()
const totalPrice = cart.totalPrice()
this.setData({
list: items,
totalCount,
totalPrice
})
},
onCheckout() {
if (!this.data.totalCount) {
wx.showToast({ title: '购物车为空', icon: 'none' })
return
}
wx.navigateTo({ url: '/pages/confirm/index' }) // 下一阶段页
}
})
6.4 简单样式 index.wxss
.page {
padding: 20rpx;
}
.cart-item {
display: flex;
background: #fff;
padding: 20rpx;
margin-bottom: 20rpx;
border-radius: 16rpx;
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.05);
}
.thumb {
width: 100rpx;
height: 100rpx;
border-radius: 8rpx;
margin-right: 20rpx;
}
.info {
display: flex;
flex-direction: column;
justify-content: space-around;
}
.name {
font-weight: bold;
font-size: 32rpx;
}
.price {
color: #fa541c;
}
.count {
font-size: 28rpx;
color: #888;
}
.bottom-bar {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
background: #fff;
padding: 20rpx;
display: flex;
justify-content: space-between;
box-shadow: 0 -2rpx 12rpx rgba(0,0,0,0.1);
}
7. TabBar 配置示例
// app.json(节选)
"tabBar": {
"list": [
{ "pagePath": "pages/index/index", "text": "菜单", "iconPath": "images/icons/home.png", "selectedIconPath": "images/icons/home-active.png" },
{ "pagePath": "pages/cart/index", "text": "购物车", "iconPath": "images/icons/business.png", "selectedIconPath": "images/icons/business-active.png" }
]
}
8. 自测清单 & Git Tag
- 首页点两道菜 → 角标显示 2
- 进入购物车页
- 看得到两条记录
- 关闭小程序再打开 → 数据依旧存在
- 一切通过后:
git add .
git commit -m "feat: shopping cart"
git tag v2.0-cart
git push --tags
9. 练习(进阶挑战)
难度 | 练习内容 |
---|---|
⭐ | 给 cart.js 增加 clear() 方法,在购物车页提供“一键清空”。 |
⭐⭐ | 在首页卡片上显示当前已选数量(小圆角徽标)。 |
⭐⭐⭐ | 把 Store 升级为 Pinia 或 Remax Recoil,体验响应式自动刷新。 |
阶段小结
- 你已拥有 加入购物车 → 全局状态 → 本地持久化 → 购物车 UI 的完整链路。
- 代码量 ≈ 250 行,但逻辑清晰、易维护。
- 接下来进入 阶段 3 – 下单 & 云数据库:把购物车内容写入
orders
集合,实现真正的下单流程。
继续加油,愉快编码!