【源码】Vue3 + Vite 开发移动端商城小程序 UI:手把手从项目初始化到上线
本文详细记录了一个完整移动端商城小程序的 Vue3 开发全过程,包含首页、分类页、商品详情、购物车、个人中心五大核心页面,附完整源码。
一、项目成果展示
先看效果(手机模拟器截图):
📱 首页
- 顶部搜索栏 + 轮播 Banner(自动轮播 3s)
- 5 列分类图标入口
- 限时活动卡片(渐变背景)
- 热销推荐(2 列卡片网格)
- 猜你喜欢(列表流)
📱 分类页
- 左侧分类导航(10 个类目)
- 右侧商品网格
- 联动切换,数据实时过滤
📱 商品详情页
- Sticky 导航栏
- SKU 选择器(颜色 + 尺码)
- 数量加减控件
- 固定底部操作栏(加入购物车 / 立即购买)
- Toast 动画提示
📱 购物车
- Pinia 状态管理(加入 / 删除 / 全选 / 编辑模式)
- 动态数量控制
- 空购物车引导
- 底部结算栏
📱 个人中心
- 渐变用户头部
- 资产栏(优惠券 / 余额 / 积分)
- 订单入口(5 种状态)
- 功能菜单列表
二、技术栈选型
| 技术 | 版本 | 用途 |
|---|---|---|
| Vue 3.4 | ^3.4.0 | 核心框架,Composition API + <script setup> |
| TypeScript | ^5.3.0 | 类型安全,开发体验提升 |
| Vite 5 | ^5.1.0 | 极速构建,HMR 热更新 |
| Vue Router 4 | ^4.3.0 | 路由管理,支持动态路由 |
| Pinia | ^2.1.7 | 状态管理,轻量替代 Vuex |
| SCSS | ^1.70.0 | 样式预处理器,变量复用 |
为什么选 Vue3 而不是 UniApp?
- UniApp 语法与 Vue3 有差异,HBuilderX 生态封闭
- Vue3 + Vite 可以直接在浏览器预览,调试效率更高
- 同一套代码,编译后可直接嵌入 UniApp 或微信小程序 web-view
三、项目初始化
# 创建项目
npm create vite@latest shop-basic -- --template vue-ts
cd shop-basic
# 安装依赖
npm install vue vue-router@4 pinia
npm install -D sass @vitejs/plugin-vue vue-tsc
# 启动开发
npm run dev
配置 Vite 支持 SCSS 全局变量注入(无需每个文件单独 import):
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss";`
}
}
}
})
四、主题色系统
定义一套统一的 SCSS 变量,后续改主题色只改一处:
// src/styles/variables.scss
$primary: #e93323; // 主题红(电商经典色)
$primary-light: #ff6b5b;
$primary-dark: #c0281a;
$bg: #f5f5f5; // 页面背景灰
$text-primary: #333; // 主文字
$text-secondary: #666;
$text-hint: #999;
$border: #f0f0f0;
$white: #fff;
五、Phone Shell 设计模式
为了在小程序和 H5 都能展示,我们给页面套上一层手机壳:
<!-- App.vue -->
<template>
<div class="app-container">
<div class="phone-shell">
<!-- 状态栏 -->
<div class="status-bar">
<span>9:41</span>
<span>📶 🔋</span>
</div>
<!-- 页面内容 -->
<div class="page-content">
<router-view />
</div>
<!-- TabBar -->
<div class="tab-bar">
<!-- 底部导航项 -->
</div>
</div>
</div>
</template>
<style lang="scss">
.app-container {
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.phone-shell {
width: 390px;
height: 844px; // iPhone 12 尺寸
background: #fff;
border-radius: 40px;
overflow: hidden;
box-shadow: 0 30px 80px rgba(0,0,0,.6);
display: flex;
flex-direction: column;
}
</style>
这样每个页面渲染出来就是一个完整的"小程序截图",非常适合做演示和闲鱼上架图。
六、首页完整实现
<!-- src/pages/index/index.vue -->
<template>
<div class="index-page">
<!-- 搜索栏 -->
<div class="search-bar" @click="router.push('/search')">
<svg width="16" height="16" viewBox="..."/>
<span class="placeholder">搜索商品</span>
</div>
<!-- 轮播图 -->
<div class="banner">
<div class="banner-track" :style="{ transform: `translateX(-${bannerIndex * 100}%)` }">
<div v-for="item in banners" :key="item.id" class="banner-slide">
<img :src="item.image" />
</div>
</div>
<div class="banner-dots">
<span v-for="(_, i) in banners" :class="{ active: i === bannerIndex }" @click="bannerIndex = i" />
</div>
</div>
<!-- 分类入口 -->
<div class="category-entry">
<div v-for="cat in categories" :key="cat.id" class="cat-item" @click="goCategory(cat.id)">
<div class="cat-icon" :style="{ background: cat.color }">
<span>{{ cat.emoji }}</span>
</div>
<span>{{ cat.name }}</span>
</div>
</div>
<!-- 热销推荐 -->
<div class="section">
<div class="section-header">
<span class="title">🛒 热销推荐</span>
<span class="more" @click="goCategory()">查看更多 ›</span>
</div>
<div class="goods-grid">
<div v-for="goods in hotGoods" :key="goods.id" class="goods-card" @click="goDetail(goods.id)">
<div class="goods-img" :style="{ background: goods.bgColor }">
<span class="goods-emoji">{{ goods.emoji }}</span>
<span v-if="goods.tag" class="goods-tag">{{ goods.tag }}</span>
</div>
<div class="goods-info">
<p class="goods-title">{{ goods.title }}</p>
<div class="goods-price">
<span class="price">¥{{ goods.price }}</span>
<span class="origin-price" v-if="goods.originalPrice > goods.price">¥{{ goods.originalPrice }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
const bannerIndex = ref(0)
let timer: ReturnType<typeof setInterval>
// 自动轮播
onMounted(() => {
timer = setInterval(() => {
bannerIndex.value = (bannerIndex.value + 1) % banners.value.length
}, 3000)
})
onUnmounted(() => clearInterval(timer))
const banners = ref([...])
const categories = ref([...])
const hotGoods = ref([...])
const goDetail = (id: number) => router.push(`/goods/${id}`)
</script>
七、Pinia 购物车状态管理
核心亮点:Composition API 风格的 Store,支持完整的增删改查:
// src/stores/cart.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export interface CartItem {
id: number
goodsId: number
title: string
price: number
quantity: number
sku: string
checked: boolean
}
export const useCartStore = defineStore('cart', () => {
const cartList = ref<CartItem[]>([])
const checkedItems = computed(() => cartList.value.filter(item => item.checked))
const totalCount = computed(() =>
cartList.value.reduce((sum, item) => sum + item.quantity, 0)
)
const totalPrice = computed(() =>
checkedItems.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
)
const isAllChecked = computed(
() => cartList.value.length > 0 && cartList.value.every(item => item.checked)
)
function addItem(goodsId: number, color: string, size: string, qty: number) {
const existing = cartList.value.find(
i => i.goodsId === goodsId && i.sku === `${color} / ${size}`
)
if (existing) {
existing.quantity += qty
} else {
cartList.value.push({ id: Date.now(), goodsId, title: '...', price: 89, quantity: qty, sku: `${color} / ${size}`, checked: true })
}
}
function toggleCheckAll() {
const newVal = !isAllChecked.value
cartList.value.forEach(item => { item.checked = newVal })
}
return { cartList, totalCount, totalPrice, isAllChecked, addItem, toggleCheckAll, ... }
})
在组件中使用:
<script setup>
import { useCartStore } from '@/stores/cart'
const cartStore = useCartStore()
const { cartList, totalPrice, isAllChecked } = storeToRefs(cartStore)
</script>
八、商品详情页实现
<!-- src/pages/goods/detail.vue -->
<template>
<div class="detail-page">
<!-- SKU 选择 -->
<div class="sku-section">
<div class="sku-group">
<span class="sku-label">颜色</span>
<div class="sku-options">
<span v-for="color in goods.colors" :key="color"
class="sku-tag" :class="{ active: selectedColor === color }"
@click="selectedColor = color">
{{ color }}
</span>
</div>
</div>
<div class="quantity-row">
<span class="sku-label">数量</span>
<div class="qty-control">
<button @click="qty = Math.max(1, qty - 1)">-</button>
<span>{{ qty }}</span>
<button @click="qty++">+</button>
</div>
</div>
</div>
<!-- 底部操作栏 -->
<div class="action-bar">
<button class="btn-cart" @click="addCart">加入购物车</button>
<button class="btn-buy" @click="buyNow">立即购买</button>
</div>
</div>
<transition name="fade">
<div class="toast" v-if="showToast">{{ toastMsg }}</div>
</transition>
</template>
<script setup>
import { useCartStore } from '@/stores/cart'
const cartStore = useCartStore()
function addCart() {
cartStore.addItem(goods.id, selectedColor.value, selectedSize.value, qty.value)
toast('已加入购物车 🛒')
}
</script>
九、路由配置
// src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
export default createRouter({
history: createWebHistory(),
routes: [
{ path: '/', component: () => import('../pages/index/index.vue') },
{ path: '/category', component: () => import('../pages/category/index.vue') },
{ path: '/goods/:id', component: () => import('../pages/goods/detail.vue') },
{ path: '/cart', component: () => import('../pages/cart/index.vue') },
{ path: '/user', component: () => import('../pages/user/index.vue') },
],
})
十、部署与后续扩展
构建生产版本:
npm run build
# 输出到 dist/ 目录,可直接部署到 Vercel / Netlify / 静态托管
接入真实后端:
- 将
src/api/中的接口地址替换为真实 API - 接入微信支付 / 支付宝支付
- 添加用户登录(微信静默授权)
嵌入微信小程序:
- 使用 uni-app 的 wgt 打包模式
- 或使用 web-view 直接嵌套 H5 页面
十一、源码获取
完整源码已整理为可直接运行的项目包,包含:
- ✅ 5 个完整页面(首页 / 分类 / 详情 / 购物车 / 个人中心)
- ✅ Pinia 状态管理(购物车 + 用户)
- ✅ SCSS 主题色系统
- ✅ 手机壳 UI 展示层
- ✅ Vite 构建配置
闲鱼搜索「源码老李-Vue3通用商城小程序UI」即可找到,价格实惠,支持毕业设计 / 作品集 / 二次开发。
十二、总结
这个项目展示了 Vue3 + Vite 开发移动端商城小程序的完整方案,核心亮点:
- Composition API +
<script setup>— 代码简洁,逻辑清晰 - Pinia 状态管理 — 购物车逻辑完整,支持批量操作
- SCSS 变量系统 — 一处改色,全局生效
- Phone Shell 模式 — 同时适配小程序展示和 H5 开发
- 零后端依赖 — 纯前端 UI,可直接对接任意后端 API
希望这篇文章对你有帮助!如果想要更多功能(订单系统、优惠券、地址管理),可以在此基础上继续扩展。
标签: Vue3 Vite TypeScript Pinia 商城小程序 前端开发 毕业设计 SCSS