uni-app搭配Pinia实战构建跨端购物车状态管理全流程在移动应用开发中购物车功能几乎是电商类应用的标配。随着跨平台开发框架的普及如何在uni-app中优雅地实现购物车状态管理成为开发者关注的焦点。本文将带你从零开始使用Pinia构建一个完整的跨端购物车模块涵盖商品列表、加入购物车、数量增减和状态持久化等核心功能。1. 环境准备与项目初始化首先确保你已经安装了最新版本的HBuilderX或配置好了uni-app开发环境。创建一个新的uni-app项目选择Vue3版本Pinia在Vue2中支持有限。安装必要的依赖npm install pinia pinia/nuxt项目目录结构建议如下├── pages ├── static ├── stores │ ├── cart.js │ └── products.js ├── App.vue ├── main.js ├── manifest.json └── pages.json在main.js中初始化Piniaimport { createSSRApp } from vue import { createPinia } from pinia import App from ./App.vue export function createApp() { const app createSSRApp(App) const pinia createPinia() app.use(pinia) return { app, pinia } }2. 设计购物车状态管理架构在stores目录下创建cart.js定义购物车的核心状态和操作import { defineStore } from pinia import { ref, computed } from vue export const useCartStore defineStore(cart, () { // 购物车商品列表 const items ref([]) // 计算总数量 const totalQuantity computed(() { return items.value.reduce((sum, item) sum item.quantity, 0) }) // 计算总金额 const totalAmount computed(() { return items.value.reduce((sum, item) sum (item.price * item.quantity), 0) }) // 添加商品到购物车 const addItem (product) { const existingItem items.value.find(item item.id product.id) if (existingItem) { existingItem.quantity 1 } else { items.value.push({ ...product, quantity: 1 }) } // 持久化存储 uni.setStorageSync(cart_items, JSON.stringify(items.value)) } // 从购物车移除商品 const removeItem (productId) { items.value items.value.filter(item item.id ! productId) uni.setStorageSync(cart_items, JSON.stringify(items.value)) } // 更新商品数量 const updateQuantity (productId, quantity) { const item items.value.find(item item.id productId) if (item) { item.quantity quantity uni.setStorageSync(cart_items, JSON.stringify(items.value)) } } // 初始化时从本地存储加载 const loadFromStorage () { const storedItems uni.getStorageSync(cart_items) if (storedItems) { items.value JSON.parse(storedItems) } } return { items, totalQuantity, totalAmount, addItem, removeItem, updateQuantity, loadFromStorage } })3. 商品列表与购物车交互实现创建商品列表页面展示可购买商品并与购物车交互template view classproduct-list view v-forproduct in products :keyproduct.id classproduct-item image :srcproduct.image modeaspectFill / view classproduct-info text classproduct-name{{ product.name }}/text text classproduct-price¥{{ product.price }}/text /view button clickaddToCart(product)加入购物车/button /view /view /template script setup import { ref, onMounted } from vue import { useCartStore } from /stores/cart const cartStore useCartStore() const products ref([ { id: 1, name: 商品1, price: 99, image: /static/product1.jpg }, { id: 2, name: 商品2, price: 199, image: /static/product2.jpg }, { id: 3, name: 商品3, price: 299, image: /static/product3.jpg } ]) const addToCart (product) { cartStore.addItem(product) uni.showToast({ title: 已加入购物车, icon: success }) } /script style scoped .product-list { padding: 20px; } .product-item { margin-bottom: 20px; border: 1px solid #eee; border-radius: 8px; overflow: hidden; } .product-info { padding: 10px; } .product-name { font-size: 16px; font-weight: bold; } .product-price { color: #f44; font-size: 18px; margin-top: 5px; display: block; } /style4. 购物车页面实现与跨端适配创建购物车页面展示已选商品并支持数量调整template view classcart-container view v-ifcartStore.items.length 0 classempty-cart text购物车是空的/text button clickgoToProductList去逛逛/button /view view v-else view v-foritem in cartStore.items :keyitem.id classcart-item image :srcitem.image modeaspectFill / view classitem-info text classitem-name{{ item.name }}/text text classitem-price¥{{ item.price }}/text /view view classquantity-control button clickdecreaseQuantity(item.id) :disableditem.quantity 1-/button text{{ item.quantity }}/text button clickincreaseQuantity(item.id)/button /view button classremove-btn clickremoveItem(item.id)删除/button /view view classcart-summary text共 {{ cartStore.totalQuantity }} 件/text text合计: ¥{{ cartStore.totalAmount }}/text button classcheckout-btn结算/button /view /view /view /template script setup import { useCartStore } from /stores/cart import { onShow } from dcloudio/uni-app const cartStore useCartStore() // 每次页面显示时从存储加载 onShow(() { cartStore.loadFromStorage() }) const increaseQuantity (productId) { const item cartStore.items.find(item item.id productId) if (item) { cartStore.updateQuantity(productId, item.quantity 1) } } const decreaseQuantity (productId) { const item cartStore.items.find(item item.id productId) if (item item.quantity 1) { cartStore.updateQuantity(productId, item.quantity - 1) } } const removeItem (productId) { cartStore.removeItem(productId) } const goToProductList () { uni.switchTab({ url: /pages/product/list }) } /script style scoped .cart-container { padding: 20px; } .empty-cart { text-align: center; padding: 50px 0; } .cart-item { display: flex; align-items: center; margin-bottom: 15px; padding: 10px; border: 1px solid #eee; border-radius: 8px; } .cart-item image { width: 80px; height: 80px; margin-right: 10px; } .item-info { flex: 1; } .item-name { font-size: 14px; } .item-price { color: #f44; font-size: 16px; margin-top: 5px; display: block; } .quantity-control { display: flex; align-items: center; margin: 0 10px; } .quantity-control button { width: 30px; height: 30px; line-height: 30px; padding: 0; } .quantity-control text { margin: 0 10px; } .remove-btn { background-color: #f44; color: white; } .cart-summary { position: fixed; bottom: 0; left: 0; right: 0; display: flex; justify-content: space-between; align-items: center; padding: 10px 20px; background-color: white; border-top: 1px solid #eee; } .checkout-btn { background-color: #f44; color: white; } /style5. 跨端适配与性能优化针对不同平台的特性我们需要做一些适配工作H5平台注意事项使用localStorage代替uni-app的存储API添加路由守卫防止直接访问购物车页面小程序平台注意事项注意小程序中的页面栈限制优化图片大小减少包体积性能优化建议防抖处理对频繁操作如数量增减添加防抖import { debounce } from lodash-es const updateQuantity debounce((productId, quantity) { // 实际更新逻辑 }, 300)虚拟列表商品数量多时使用虚拟列表优化渲染uv-list :datacartStore.items :height500 template v-slot{ item } !-- 商品项渲染 -- /template /uv-list状态持久化策略// 在app.vue中监听应用状态 onLaunch(() { const cartStore useCartStore() cartStore.loadFromStorage() // 应用隐藏时保存状态 uni.onAppHide(() { uni.setStorageSync(cart_items, JSON.stringify(cartStore.items)) }) })6. 高级功能扩展优惠券与折扣计算// 在cart.js中添加 const discount ref(0) const applyCoupon (code) { // 验证优惠券逻辑 discount.value 10 // 假设优惠10元 } const finalAmount computed(() { return Math.max(0, totalAmount.value - discount.value) })多店铺购物车支持const shops ref({}) const addItem (product, shopId) { if (!shops.value[shopId]) { shops.value[shopId] [] } // 添加到对应店铺 const existingItem shops.value[shopId].find(item item.id product.id) // ...其余逻辑 }购物车数据同步const syncCartToServer async () { try { const res await uni.request({ url: https://api.example.com/cart/sync, method: POST, data: { items: items.value, userId: getUserId() } }) if (res.data.success) { uni.showToast({ title: 同步成功 }) } } catch (error) { console.error(同步失败:, error) } }在实际项目中购物车功能的复杂度会根据业务需求而变化。通过Pinia的状态管理我们可以保持代码的组织性和可维护性同时轻松应对各种业务场景的扩展。
uni-app搭配Pinia实战:手把手教你构建一个跨端购物车状态管理(含完整代码)
发布时间:2026/6/11 20:23:54
uni-app搭配Pinia实战构建跨端购物车状态管理全流程在移动应用开发中购物车功能几乎是电商类应用的标配。随着跨平台开发框架的普及如何在uni-app中优雅地实现购物车状态管理成为开发者关注的焦点。本文将带你从零开始使用Pinia构建一个完整的跨端购物车模块涵盖商品列表、加入购物车、数量增减和状态持久化等核心功能。1. 环境准备与项目初始化首先确保你已经安装了最新版本的HBuilderX或配置好了uni-app开发环境。创建一个新的uni-app项目选择Vue3版本Pinia在Vue2中支持有限。安装必要的依赖npm install pinia pinia/nuxt项目目录结构建议如下├── pages ├── static ├── stores │ ├── cart.js │ └── products.js ├── App.vue ├── main.js ├── manifest.json └── pages.json在main.js中初始化Piniaimport { createSSRApp } from vue import { createPinia } from pinia import App from ./App.vue export function createApp() { const app createSSRApp(App) const pinia createPinia() app.use(pinia) return { app, pinia } }2. 设计购物车状态管理架构在stores目录下创建cart.js定义购物车的核心状态和操作import { defineStore } from pinia import { ref, computed } from vue export const useCartStore defineStore(cart, () { // 购物车商品列表 const items ref([]) // 计算总数量 const totalQuantity computed(() { return items.value.reduce((sum, item) sum item.quantity, 0) }) // 计算总金额 const totalAmount computed(() { return items.value.reduce((sum, item) sum (item.price * item.quantity), 0) }) // 添加商品到购物车 const addItem (product) { const existingItem items.value.find(item item.id product.id) if (existingItem) { existingItem.quantity 1 } else { items.value.push({ ...product, quantity: 1 }) } // 持久化存储 uni.setStorageSync(cart_items, JSON.stringify(items.value)) } // 从购物车移除商品 const removeItem (productId) { items.value items.value.filter(item item.id ! productId) uni.setStorageSync(cart_items, JSON.stringify(items.value)) } // 更新商品数量 const updateQuantity (productId, quantity) { const item items.value.find(item item.id productId) if (item) { item.quantity quantity uni.setStorageSync(cart_items, JSON.stringify(items.value)) } } // 初始化时从本地存储加载 const loadFromStorage () { const storedItems uni.getStorageSync(cart_items) if (storedItems) { items.value JSON.parse(storedItems) } } return { items, totalQuantity, totalAmount, addItem, removeItem, updateQuantity, loadFromStorage } })3. 商品列表与购物车交互实现创建商品列表页面展示可购买商品并与购物车交互template view classproduct-list view v-forproduct in products :keyproduct.id classproduct-item image :srcproduct.image modeaspectFill / view classproduct-info text classproduct-name{{ product.name }}/text text classproduct-price¥{{ product.price }}/text /view button clickaddToCart(product)加入购物车/button /view /view /template script setup import { ref, onMounted } from vue import { useCartStore } from /stores/cart const cartStore useCartStore() const products ref([ { id: 1, name: 商品1, price: 99, image: /static/product1.jpg }, { id: 2, name: 商品2, price: 199, image: /static/product2.jpg }, { id: 3, name: 商品3, price: 299, image: /static/product3.jpg } ]) const addToCart (product) { cartStore.addItem(product) uni.showToast({ title: 已加入购物车, icon: success }) } /script style scoped .product-list { padding: 20px; } .product-item { margin-bottom: 20px; border: 1px solid #eee; border-radius: 8px; overflow: hidden; } .product-info { padding: 10px; } .product-name { font-size: 16px; font-weight: bold; } .product-price { color: #f44; font-size: 18px; margin-top: 5px; display: block; } /style4. 购物车页面实现与跨端适配创建购物车页面展示已选商品并支持数量调整template view classcart-container view v-ifcartStore.items.length 0 classempty-cart text购物车是空的/text button clickgoToProductList去逛逛/button /view view v-else view v-foritem in cartStore.items :keyitem.id classcart-item image :srcitem.image modeaspectFill / view classitem-info text classitem-name{{ item.name }}/text text classitem-price¥{{ item.price }}/text /view view classquantity-control button clickdecreaseQuantity(item.id) :disableditem.quantity 1-/button text{{ item.quantity }}/text button clickincreaseQuantity(item.id)/button /view button classremove-btn clickremoveItem(item.id)删除/button /view view classcart-summary text共 {{ cartStore.totalQuantity }} 件/text text合计: ¥{{ cartStore.totalAmount }}/text button classcheckout-btn结算/button /view /view /view /template script setup import { useCartStore } from /stores/cart import { onShow } from dcloudio/uni-app const cartStore useCartStore() // 每次页面显示时从存储加载 onShow(() { cartStore.loadFromStorage() }) const increaseQuantity (productId) { const item cartStore.items.find(item item.id productId) if (item) { cartStore.updateQuantity(productId, item.quantity 1) } } const decreaseQuantity (productId) { const item cartStore.items.find(item item.id productId) if (item item.quantity 1) { cartStore.updateQuantity(productId, item.quantity - 1) } } const removeItem (productId) { cartStore.removeItem(productId) } const goToProductList () { uni.switchTab({ url: /pages/product/list }) } /script style scoped .cart-container { padding: 20px; } .empty-cart { text-align: center; padding: 50px 0; } .cart-item { display: flex; align-items: center; margin-bottom: 15px; padding: 10px; border: 1px solid #eee; border-radius: 8px; } .cart-item image { width: 80px; height: 80px; margin-right: 10px; } .item-info { flex: 1; } .item-name { font-size: 14px; } .item-price { color: #f44; font-size: 16px; margin-top: 5px; display: block; } .quantity-control { display: flex; align-items: center; margin: 0 10px; } .quantity-control button { width: 30px; height: 30px; line-height: 30px; padding: 0; } .quantity-control text { margin: 0 10px; } .remove-btn { background-color: #f44; color: white; } .cart-summary { position: fixed; bottom: 0; left: 0; right: 0; display: flex; justify-content: space-between; align-items: center; padding: 10px 20px; background-color: white; border-top: 1px solid #eee; } .checkout-btn { background-color: #f44; color: white; } /style5. 跨端适配与性能优化针对不同平台的特性我们需要做一些适配工作H5平台注意事项使用localStorage代替uni-app的存储API添加路由守卫防止直接访问购物车页面小程序平台注意事项注意小程序中的页面栈限制优化图片大小减少包体积性能优化建议防抖处理对频繁操作如数量增减添加防抖import { debounce } from lodash-es const updateQuantity debounce((productId, quantity) { // 实际更新逻辑 }, 300)虚拟列表商品数量多时使用虚拟列表优化渲染uv-list :datacartStore.items :height500 template v-slot{ item } !-- 商品项渲染 -- /template /uv-list状态持久化策略// 在app.vue中监听应用状态 onLaunch(() { const cartStore useCartStore() cartStore.loadFromStorage() // 应用隐藏时保存状态 uni.onAppHide(() { uni.setStorageSync(cart_items, JSON.stringify(cartStore.items)) }) })6. 高级功能扩展优惠券与折扣计算// 在cart.js中添加 const discount ref(0) const applyCoupon (code) { // 验证优惠券逻辑 discount.value 10 // 假设优惠10元 } const finalAmount computed(() { return Math.max(0, totalAmount.value - discount.value) })多店铺购物车支持const shops ref({}) const addItem (product, shopId) { if (!shops.value[shopId]) { shops.value[shopId] [] } // 添加到对应店铺 const existingItem shops.value[shopId].find(item item.id product.id) // ...其余逻辑 }购物车数据同步const syncCartToServer async () { try { const res await uni.request({ url: https://api.example.com/cart/sync, method: POST, data: { items: items.value, userId: getUserId() } }) if (res.data.success) { uni.showToast({ title: 同步成功 }) } } catch (error) { console.error(同步失败:, error) } }在实际项目中购物车功能的复杂度会根据业务需求而变化。通过Pinia的状态管理我们可以保持代码的组织性和可维护性同时轻松应对各种业务场景的扩展。