Vue + Vant 实战:手把手教你从零搭建一个电商App(含完整代码)

张开发
2026/5/24 7:39:56 15 分钟阅读
Vue + Vant 实战:手把手教你从零搭建一个电商App(含完整代码)
Vue Vant 电商App实战从架构设计到打包上线的完整指南移动互联网时代电商类应用依然是技术实践的最佳场景之一。作为前端开发者掌握Vue生态与移动端组件库的深度结合能够快速构建出体验优秀的商业应用。本文将带你从零开始使用Vue 3和Vant 4打造一个功能完整的电商App涵盖架构设计、核心模块实现、性能优化到最终打包上线的全流程。1. 项目初始化与工程配置1.1 现代Vue项目脚手架搭建不同于传统的Vue CLI初始化方式我们采用Vite作为构建工具它能带来更快的冷启动和热更新速度。首先确保你的Node.js版本在16.0以上npm create vitelatest vue-mall --template vue-ts cd vue-mall npm install接下来添加Vant 4的按需引入支持npm install vantnext npm install unplugin-vue-components -D配置vite.config.ts实现自动按需导入import { defineConfig } from vite import vue from vitejs/plugin-vue import Components from unplugin-vue-components/vite import { VantResolver } from unplugin-vue-components/resolvers export default defineConfig({ plugins: [ vue(), Components({ resolvers: [VantResolver()], }), ], css: { preprocessorOptions: { less: { javascriptEnabled: true, }, }, }, })1.2 移动端适配方案电商应用需要完美适配各种移动设备推荐使用postcss-px-to-viewport方案npm install postcss-px-to-viewport -D创建postcss.config.jsmodule.exports { plugins: { postcss-px-to-viewport: { viewportWidth: 375, unitPrecision: 5, propList: [*], selectorBlackList: [/^\.van-/], }, }, }注意这里排除了Vant组件内部样式的转换因为Vant已经做好了移动端适配2. 核心架构设计与实现2.1 模块化路由配置电商App通常包含首页、分类、购物车、个人中心等核心模块。我们采用Vue Router 4的TypeScript支持// router/index.ts import { createRouter, createWebHistory } from vue-router const routes [ { path: /, component: () import(/views/layout/index.vue), children: [ { path: , name: Home, component: () import(/views/home/index.vue), meta: { title: 首页, keepAlive: true } }, { path: category, name: Category, component: () import(/views/category/index.vue), meta: { title: 分类 } }, { path: cart, name: Cart, component: () import(/views/cart/index.vue), meta: { title: 购物车 } }, { path: mine, name: Mine, component: () import(/views/mine/index.vue), meta: { title: 我的 } } ] }, { path: /login, name: Login, component: () import(/views/login/index.vue), meta: { title: 登录 } } ] const router createRouter({ history: createWebHistory(), routes, scrollBehavior(to, from, savedPosition) { return savedPosition || { top: 0 } } }) export default router2.2 状态管理方案对于电商应用购物车状态、用户信息等需要全局共享。我们采用Pinia作为状态管理库// stores/cart.ts import { defineStore } from pinia interface CartItem { id: string name: string price: number count: number image: string selected: boolean } export const useCartStore defineStore(cart, { state: () ({ items: [] as CartItem[], total: 0 }), getters: { selectedItems: (state) state.items.filter(item item.selected), totalPrice: (state) { return state.items.reduce((sum, item) { return item.selected ? sum item.price * item.count : sum }, 0) } }, actions: { addItem(item: OmitCartItem, count | selected) { const existing this.items.find(i i.id item.id) if (existing) { existing.count } else { this.items.push({ ...item, count: 1, selected: true }) } this.calculateTotal() }, removeItem(id: string) { this.items this.items.filter(item item.id ! id) this.calculateTotal() }, calculateTotal() { this.total this.items.reduce((sum, item) sum item.count, 0) } } })3. 核心功能模块实现3.1 首页设计与实现电商首页通常包含轮播图、商品分类入口、促销活动等模块。使用Vant组件可以快速搭建script setup langts import { ref, onMounted } from vue import { getHomeData } from /api/home import type { BannerItem, CategoryItem, HotItem } from /types/home const banners refBannerItem[]([]) const categories refCategoryItem[]([]) const hotList refHotItem[]([]) onMounted(async () { const data await getHomeData() banners.value data.banners categories.value data.categories hotList.value data.hotList }) /script template div classhome-page van-sticky van-search shaperound background#ff0036 placeholder搜索商品 clickrouter.push(/search) / /van-sticky van-swipe classbanner :autoplay3000 indicator-color#ff0036 van-swipe-item v-foritem in banners :keyitem.id img :srcitem.imageUrl / /van-swipe-item /van-swipe van-grid :column-num5 :borderfalse van-grid-item v-foritem in categories :keyitem.id :iconitem.icon :textitem.name / /van-grid section classhot-section h3 classsection-title限时抢购/h3 van-count-down :time24 * 60 * 60 * 1000 template #defaulttimeData span classcountdown-item{{ timeData.hours }}/span: span classcountdown-item{{ timeData.minutes }}/span: span classcountdown-item{{ timeData.seconds }}/span /template /van-count-down van-grid :column-num2 :gutter10 van-grid-item v-foritem in hotList :keyitem.id goods-card :dataitem / /van-grid-item /van-grid /section /div /template3.2 商品详情页实现商品详情页需要处理SKU选择、加入购物车等复杂交互script setup langts import { ref, computed } from vue import { useRoute } from vue-router import { getGoodsDetail } from /api/goods import { useCartStore } from /stores/cart const route useRoute() const cartStore useCartStore() const goods refany(null) const showSku ref(false) const selectedSku refany(null) const price computed(() { return selectedSku.value?.price || goods.value?.minPrice }) onMounted(async () { const { id } route.params goods.value await getGoodsDetail(id as string) }) const handleAddCart () { if (!selectedSku.value) { showSku.value true return } cartStore.addItem({ id: ${goods.value.id}-${selectedSku.value.id}, name: goods.value.name, price: selectedSku.value.price, image: goods.value.mainPictures[0] }) Toast.success(添加成功) } /script template div classgoods-detail van-swipe classpreview-swipe van-swipe-item v-forimage in goods?.mainPictures :keyimage img :srcimage / /van-swipe-item /van-swipe div classgoods-info h2 classtitle{{ goods?.name }}/h2 p classdesc{{ goods?.desc }}/p div classprice-section span classprice¥{{ price }}/span van-button typeprimary sizesmall clickshowSku true 选择规格 /van-button /div /div van-sku v-modelshowSku :skugoods?.sku :goodsgoods sku-selectedselectedSku $event / van-action-bar van-action-bar-icon iconchat-o text客服 / van-action-bar-icon iconcart-o :badgecartStore.total text购物车 / van-action-bar-button typewarning text加入购物车 clickhandleAddCart / van-action-bar-button typedanger text立即购买 / /van-action-bar /div /template4. 性能优化与打包部署4.1 关键性能优化策略电商应用需要特别关注首屏加载速度和运行时性能路由懒加载已经通过动态import()实现图片懒加载使用Vant的Lazyload组件API请求优化// utils/request.ts import axios from axios import { Toast } from vant const instance axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, timeout: 10000, headers: { Cache-Control: max-age300 } }) instance.interceptors.response.use( response { if (response.data.code ! 200) { Toast.fail(response.data.message) return Promise.reject(response.data) } return response.data.data }, error { Toast.fail(网络错误请稍后重试) return Promise.reject(error) } ) export default instance组件级缓存router-view v-slot{ Component } keep-alive :include[Home, Category] component :isComponent / /keep-alive /router-view4.2 打包与移动端适配使用Vite构建生产环境包npm run build配置vite.config.ts中的base路径export default defineConfig({ base: ./, build: { assetsInlineLimit: 4096, chunkSizeWarningLimit: 1000, rollupOptions: { output: { manualChunks(id) { if (id.includes(node_modules)) { return vendor } } } } } })对于需要打包成APK的场景可以使用Capacitor或Cordovanpm install capacitor/core capacitor/cli -D npx cap init npx cap add android npx cap sync5. 项目扩展与进阶方向5.1 微前端架构探索当电商业务复杂度增加时可以考虑微前端架构// 主应用配置 import { registerMicroApps, start } from qiankun registerMicroApps([ { name: product, entry: //localhost:7101, container: #subapp-container, activeRule: /product, }, { name: order, entry: //localhost:7102, container: #subapp-container, activeRule: /order, } ]) start()5.2 服务端渲染(SSR)方案对于SEO要求高的电商页面可以使用Nuxt.js或Vite SSR// vite.ssr.config.js import { defineConfig } from vite import vue from vitejs/plugin-vue export default defineConfig({ plugins: [vue()], build: { ssr: true, rollupOptions: { input: /src/entry-server.js } } })5.3 Web性能监控集成Sentry监控前端错误和性能import * as Sentry from sentry/vue import { BrowserTracing } from sentry/tracing Sentry.init({ dsn: your-dsn, integrations: [ new BrowserTracing({ routingInstrumentation: Sentry.vueRouterInstrumentation(router), tracingOrigins: [localhost, your-domain.com], }), ], tracesSampleRate: 0.2, })

更多文章