🏆 Taro+Vue3 入门(十):实战项目 — 从零搭建完整小程序
系列导读:完结篇!综合前 9 篇所有知识,搭建一个带有 首页/商品列表/购物车/我的的完整电商小程序。
🏗 1. 完整项目结构
src/
├── app.ts # 入口 + Pinia 注册
├── app.config.ts # 路由 + TabBar
├── app.scss # 全局样式变量
├── api/ # 接口模块
│ ├── request.ts
│ ├── product.ts
│ └── auth.ts
├── stores/ # Pinia Store
│ ├── auth.ts
│ └── cart.ts
├── composables/ # 组合式函数
│ └── useRequest.ts
├── utils/ # 工具
│ ├── platform.ts
│ └── storage.ts
├── components/ # 公共组件
│ ├── ProductCard.vue
│ └── Empty.vue
└── pages/
├── index/index.vue # 🏠 首页
├── list/index.vue # 📜 分类
├── cart/index.vue # 🛒 购物车
├── mine/index.vue # 👤 我的
├── detail/index.vue # 📖 商品详情
└── login/index.vue # 🔐 登录
🏠 2. 首页
<!-- src/pages/index/index.vue -->
<script setup lang="ts">
import { ref } from 'vue'
import { useDidShow } from '@tarojs/taro'
import Taro from '@tarojs/taro'
import { useRequest } from '@/composables/useRequest'
import { productApi } from '@/api/product'
import ProductCard from '@/components/ProductCard.vue'
const { data: products, refresh } = useRequest(() =>
productApi.getList({ page: 1, pageSize: 10 })
)
useDidShow(() => refresh())
const banners = ref([
{ id: 1, image: 'https://picsum.photos/750/320?1' },
{ id: 2, image: 'https://picsum.photos/750/320?2' },
{ id: 3, image: 'https://picsum.photos/750/320?3' },
])
const categories = [
{ id: 1, name: '手机', icon: '📱' },
{ id: 2, name: '电脑', icon: '💻' },
{ id: 3, name: '耳机', icon: '🎧' },
{ id: 4, name: '手表', icon: '⌚' },
{ id: 5, name: '更多', icon: '📦' },
]
</script>
<template>
<view class="home">
<!-- 搜索 -->
<nut-searchbar placeholder="搜索商品" disabled
@click-input="() => Taro.navigateTo({ url: '/pages/search/index' })"
/>
<!-- 轮播 -->
<swiper class="banner" autoplay circular :indicator-dots="true">
<swiper-item v-for="b in banners" :key="b.id">
<image :src="b.image" mode="aspectFill" class="banner-img" />
</swiper-item>
</swiper>
<!-- 分类 -->
<nut-grid :column-num="5">
<nut-grid-item v-for="cat in categories" :key="cat.id" :text="cat.name">
<text style="font-size: 40px">{{ cat.icon }}</text>
</nut-grid-item>
</nut-grid>
<!-- 推荐商品 -->
<view class="section">
<text class="section-title">🔥 热门推荐</text>
<view class="product-grid">
<ProductCard
v-for="p in products?.list"
:key="p.id"
v-bind="p"
@tap="() => Taro.navigateTo({ url: `/pages/detail/index?id=${p.id}` })"
/>
</view>
</view>
</view>
</template>
<style lang="scss">
.home {
background: #f5f5f5;
.banner { height: 320px; .banner-img { width: 100%; height: 320px; } }
.section {
background: #fff; padding: 24px; margin-top: 16px;
.section-title { font-size: 32px; font-weight: bold; }
.product-grid { display: flex; flex-wrap: wrap; gap: 16px; margin-top: 20px; }
}
}
</style>
🛒 3. 购物车页
<!-- src/pages/cart/index.vue -->
<script setup lang="ts">
import { useCartStore } from '@/stores/cart'
import { storeToRefs } from 'pinia'
import Taro from '@tarojs/taro'
const cartStore = useCartStore()
const { items, totalCount, totalPrice, isEmpty } = storeToRefs(cartStore)
</script>
<template>
<view v-if="isEmpty" class="empty">
<nut-empty description="购物车空空如也">
<nut-button type="primary" size="small"
@click="() => Taro.switchTab({ url: '/pages/list/index' })">
去逛逛
</nut-button>
</nut-empty>
</view>
<view v-else class="cart">
<nut-swipe
v-for="item in items" :key="item.id"
>
<view class="cart-item">
<image :src="item.image" class="item-img" mode="aspectFill" />
<view class="item-info">
<text class="item-name">{{ item.name }}</text>
<view class="item-bottom">
<text class="item-price">¥{{ item.price }}</text>
<nut-input-number
:model-value="item.quantity"
:min="1" :max="99"
@change="(v: number) => cartStore.updateQuantity(item.id, v)"
/>
</view>
</view>
</view>
<template #right>
<nut-button type="danger" shape="square"
@click="cartStore.removeItem(item.id)">
删除
</nut-button>
</template>
</nut-swipe>
<view class="footer">
<view class="total">
<text>合计:</text>
<text class="price">¥{{ totalPrice.toFixed(2) }}</text>
</view>
<nut-button type="primary" class="checkout-btn">
去结算({{ totalCount }})
</nut-button>
</view>
</view>
</template>
👤 4. 我的页面
<!-- src/pages/mine/index.vue -->
<script setup lang="ts">
import { useAuthStore } from '@/stores/auth'
import { storeToRefs } from 'pinia'
import Taro from '@tarojs/taro'
const authStore = useAuthStore()
const { user, isLoggedIn } = storeToRefs(authStore)
const orderItems = [
{ icon: '💳', label: '待付款', count: 2 },
{ icon: '📦', label: '待发货', count: 0 },
{ icon: '🚚', label: '待收货', count: 1 },
{ icon: '⭐', label: '待评价', count: 3 },
]
function handleLogout() {
authStore.logout()
Taro.showToast({ title: '已退出', icon: 'none' })
}
</script>
<template>
<view class="mine">
<!-- 头部 -->
<view class="header">
<template v-if="isLoggedIn">
<nut-avatar size="large" :url="user?.avatar" />
<view class="user-info">
<text class="name">{{ user?.name }}</text>
<nut-tag type="primary" plain>VIP 会员</nut-tag>
</view>
</template>
<view v-else class="login-prompt"
@tap="() => Taro.navigateTo({ url: '/pages/login/index' })">
<nut-avatar size="large" />
<text class="login-text">点击登录</text>
</view>
</view>
<!-- 订单 -->
<nut-cell title="我的订单" is-link extra="查看全部" />
<view class="order-grid">
<view v-for="item in orderItems" :key="item.label" class="order-item">
<text class="order-icon">{{ item.icon }}</text>
<text class="order-label">{{ item.label }}</text>
<text v-if="item.count" class="badge">{{ item.count }}</text>
</view>
</view>
<!-- 功能列表 -->
<view class="func-list">
<nut-cell title="收货地址" is-link />
<nut-cell title="优惠券" extra="3 张可用" is-link />
<nut-cell title="帮助中心" is-link />
<nut-cell title="关于我们" is-link />
</view>
<view v-if="isLoggedIn" class="logout">
<nut-button block plain type="danger" @click="handleLogout">退出登录</nut-button>
</view>
</view>
</template>
🚀 5. 构建发布
# 开发
npm run dev:weapp # 微信
npm run dev:h5 # H5
# 构建
npm run build:weapp # 微信小程序 → dist/
npm run build:h5 # H5 → dist/h5/
# 微信发布:开发者工具 → 上传 → 提交审核
# H5 发布:部署 dist/h5 到 Nginx / Vercel / CDN
✅ 全系列学习 Checklist
基础篇(第 1-3 篇)
- 用 Taro CLI 创建 Vue3 项目
- 掌握 Vue3 组合式 API(ref/reactive/computed/watch)
- 熟悉 Taro 内置组件和页面生命周期
核心篇(第 4-7 篇)
- 掌握路由导航和参数传递
- 会用 NutUI Vue3 版搭建界面
- 用 Pinia 管理全局状态
- 封装统一请求层
进阶篇(第 8-9 篇)
- 掌握多端条件编译
- 调用小程序原生能力
实战篇(第 10 篇)
- 搭建完整电商小程序
- 构建发布到微信/H5
🎉 恭喜完成「Taro+Vue3 入门」全部 10 篇系列!
作为 Vue 开发者,你已经完全掌握了 Taro+Vue3 多端小程序开发。 打开微信开发者工具,开始写你自己的小程序吧!
本文是「Taro+Vue3 入门」系列第 10 篇(完结篇),共 10 篇。