开发一个跨平台电商应用
跨平台电商应用开发背景
电商应用开发需要考虑多端适配问题,传统开发方式需要为iOS、Android、Web等平台分别开发,成本高且维护困难。uni-app作为基于Vue.js的跨平台框架,可以一次开发同时发布到多个平台,大幅提升开发效率。一个典型的电商应用通常包含商品展示、购物车、订单管理、支付等核心功能模块。
项目初始化与配置
使用HBuilderX创建uni-app项目时,选择默认模板即可快速搭建基础结构。项目目录中pages存放页面文件,static存放静态资源,App.vue是应用入口文件。manifest.json文件配置应用名称、图标等基本信息。
// manifest.json示例配置
{
"name": "电商商城",
"appid": "",
"description": "",
"versionName": "1.0.0",
"versionCode": "100",
"transformPx": false,
"app-plus": {
"splashscreen": {
"alwaysShowBeforeRender": true,
"autoclose": true,
"delay": 0
}
}
}
核心页面开发
首页开发
首页通常包含轮播图、商品分类、推荐商品等模块。使用uni-app的swiper组件实现轮播效果:
<template>
<view>
<swiper :indicator-dots="true" :autoplay="true" :interval="3000">
<swiper-item v-for="(item,index) in banners" :key="index">
<image :src="item.image" mode="aspectFill"></image>
</swiper-item>
</swiper>
<view class="category-list">
<view v-for="(item,index) in categories" :key="index"
@click="navToCategory(item.id)">
<image :src="item.icon"></image>
<text>{{item.name}}</text>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
banners: [
{image: '/static/banner1.jpg', link: ''},
{image: '/static/banner2.jpg', link: ''}
],
categories: [
{id: 1, name: '手机', icon: '/static/phone.png'},
{id: 2, name: '电脑', icon: '/static/computer.png'}
]
}
},
methods: {
navToCategory(id) {
uni.navigateTo({
url: `/pages/category/list?id=${id}`
})
}
}
}
</script>
商品详情页
商品详情页需要展示商品图片、价格、规格选择、评价等信息。使用uni-popup组件实现规格选择弹窗:
<template>
<view class="detail-container">
<swiper class="image-swiper" :indicator-dots="true">
<swiper-item v-for="(img,index) in goods.images" :key="index">
<image :src="img" mode="aspectFit"></image>
</swiper-item>
</swiper>
<view class="info-section">
<view class="price">¥{{goods.price}}</view>
<view class="title">{{goods.title}}</view>
<view class="sales">已售{{goods.sales}}件</view>
</view>
<view class="spec-section" @click="showSpecPopup">
<text>选择规格</text>
<uni-icons type="arrowright"></uni-icons>
</view>
<uni-popup ref="specPopup" type="bottom">
<view class="spec-popup">
<!-- 规格选择内容 -->
</view>
</uni-popup>
</view>
</template>
购物车功能实现
购物车需要管理商品选择、数量修改、全选等交互。使用Vuex进行状态管理:
// store/cart.js
const state = {
cartItems: []
}
const mutations = {
ADD_TO_CART(state, goods) {
const existingItem = state.cartItems.find(item => item.id === goods.id)
if(existingItem) {
existingItem.quantity += 1
} else {
state.cartItems.push({
...goods,
quantity: 1,
selected: true
})
}
},
UPDATE_QUANTITY(state, {id, quantity}) {
const item = state.cartItems.find(item => item.id === id)
if(item) item.quantity = quantity
}
}
export default {
namespaced: true,
state,
mutations
}
购物车页面实现:
<template>
<view>
<view class="cart-list">
<checkbox-group @change="toggleSelect">
<view v-for="item in cartItems" :key="item.id" class="cart-item">
<checkbox :value="item.id" :checked="item.selected"></checkbox>
<image :src="item.image"></image>
<view class="info">
<text>{{item.title}}</text>
<view class="price">¥{{item.price}}</view>
<view class="quantity">
<text @click="decrease(item.id)">-</text>
<input type="number" v-model="item.quantity" @blur="updateQuantity(item)">
<text @click="increase(item.id)">+</text>
</view>
</view>
</view>
</checkbox-group>
</view>
<view class="cart-footer">
<checkbox :checked="allSelected" @click="toggleAll">全选</checkbox>
<view class="total">
合计:¥{{totalPrice}}
</view>
<button @click="checkout">结算({{selectedCount}})</button>
</view>
</view>
</template>
订单与支付流程
订单确认页展示收货地址、商品清单、优惠信息等:
<template>
<view>
<view class="address-section" @click="selectAddress">
<view v-if="selectedAddress">
<text>{{selectedAddress.name}} {{selectedAddress.phone}}</text>
<text>{{selectedAddress.fullAddress}}</text>
</view>
<view v-else>
<text>请选择收货地址</text>
</view>
</view>
<view class="order-goods">
<view v-for="item in selectedItems" :key="item.id">
<image :src="item.image"></image>
<view>
<text>{{item.title}}</text>
<text>¥{{item.price}} x {{item.quantity}}</text>
</view>
</view>
</view>
<view class="order-submit">
<text>实付:¥{{totalAmount}}</text>
<button @click="createOrder">提交订单</button>
</view>
</view>
</template>
支付流程处理:
methods: {
async createOrder() {
const orderData = {
addressId: this.selectedAddress.id,
goods: this.selectedItems.map(item => ({
id: item.id,
quantity: item.quantity
}))
}
try {
const res = await uni.request({
url: '/api/orders',
method: 'POST',
data: orderData
})
this.payOrder(res.data.orderId)
} catch (error) {
uni.showToast({ title: '创建订单失败', icon: 'none' })
}
},
payOrder(orderId) {
uni.requestPayment({
provider: 'wxpay',
orderInfo: { orderId },
success: () => {
uni.redirectTo({
url: `/pages/order/result?orderId=${orderId}`
})
},
fail: (err) => {
console.error('支付失败', err)
}
})
}
}
多平台适配处理
不同平台需要处理样式和功能差异:
/* 条件编译处理平台差异 */
/* #ifdef H5 */
.header {
height: 44px;
}
/* #endif */
/* #ifdef MP-WEIXIN */
.header {
height: 48px;
padding-top: 20px;
}
/* #endif */
/* #ifdef APP-PLUS */
.header {
height: 44px;
padding-top: 20px;
}
/* #endif */
平台特定API调用示例:
// 微信小程序获取用户信息
// #ifdef MP-WEIXIN
uni.getUserProfile({
desc: '用于完善会员资料',
success: (res) => {
this.userInfo = res.userInfo
}
})
// #endif
// APP端调用原生功能
// #ifdef APP-PLUS
plus.share.sendWithSystem({
content: '分享内容',
href: 'https://example.com'
})
// #endif
性能优化策略
- 图片懒加载:
<image lazy-load :src="item.image" mode="aspectFill"></image>
- 分页加载商品列表:
async loadMore() {
if(this.loading || this.finished) return
this.loading = true
const res = await this.$http.get('/api/goods', {
page: this.page + 1,
size: 10
})
this.list = [...this.list, ...res.data.list]
this.page = res.data.page
this.finished = res.data.finished
this.loading = false
}
- 使用分包加载:
// pages.json
{
"subPackages": [
{
"root": "subpackageA",
"pages": [
{
"path": "goods/list",
"style": {}
}
]
}
]
}
数据缓存策略
使用uni-app的存储API缓存数据:
// 获取缓存数据
async getCachedData() {
try {
const cache = await uni.getStorage({ key: 'goodsCache' })
if(cache && Date.now() - cache.timestamp < 3600000) {
return cache.data
}
return null
} catch (e) {
return null
}
}
// 更新缓存
async updateCache(data) {
await uni.setStorage({
key: 'goodsCache',
data: {
data,
timestamp: Date.now()
}
})
}
用户反馈与评价系统
商品评价组件实现:
<template>
<view class="review-section">
<view class="review-header">
<text>商品评价({{reviews.length}})</text>
<text @click="showAllReviews">查看全部</text>
</view>
<view class="review-list">
<view v-for="(item,index) in reviews.slice(0,2)" :key="index">
<view class="user-info">
<image :src="item.avatar"></image>
<text>{{item.nickname}}</text>
</view>
<view class="rating">
<uni-rate :value="item.rating" readonly></uni-rate>
<text>{{item.date}}</text>
</view>
<view class="content">{{item.content}}</view>
</view>
</view>
</view>
</template>
实时通讯功能
集成WebSocket实现客服功能:
let socketTask = null
export default {
data() {
return {
messages: [],
inputMsg: ''
}
},
onLoad() {
this.connectSocket()
},
methods: {
connectSocket() {
socketTask = uni.connectSocket({
url: 'wss://example.com/chat',
success: () => {
socketTask.onMessage(res => {
this.messages.push(JSON.parse(res.data))
})
}
})
},
sendMessage() {
if(this.inputMsg.trim()) {
const msg = {
content: this.inputMsg,
timestamp: Date.now(),
fromUser: true
}
socketTask.send({
data: JSON.stringify(msg),
success: () => {
this.messages.push(msg)
this.inputMsg = ''
}
})
}
}
}
}
数据分析与监控
集成统计SDK示例:
// 在App.vue中初始化统计
export default {
onLaunch() {
// #ifdef APP-PLUS
const umeng = uni.requireNativePlugin('UMeng-Analytics')
umeng.init('your_app_key')
// #endif
// #ifdef H5
// 初始化百度统计
const _hmt = _hmt || []
;(function() {
const hm = document.createElement("script")
hm.src = "https://hm.baidu.com/hm.js?your_app_key"
const s = document.getElementsByTagName("script")[0]
s.parentNode.insertBefore(hm, s)
})()
// #endif
},
onShow() {
this.trackPageView()
},
methods: {
trackPageView() {
const pages = getCurrentPages()
const currentPage = pages[pages.length -1]
// #ifdef APP-PLUS
const umeng = uni.requireNativePlugin('UMeng-Analytics')
umeng.trackPageBegin(currentPage.route)
// #endif
// #ifdef H5
_hmt.push(['_trackPageview', currentPage.$page.fullPath])
// #endif
}
}
}
持续集成与部署
配置GitHub Actions自动化部署:
name: Build and Deploy
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: '14'
- name: Install Dependencies
run: npm install
- name: Build for Production
run: npm run build:mp-weixin
- name: Deploy to Weixin Mini Program
uses: wulabing/wechat-miniprogram-action@v1.1
with:
appid: ${{ secrets.MINI_PROGRAM_APPID }}
version: ${{ github.sha }}
desc: '自动部署'
privateKey: ${{ secrets.MINI_PROGRAM_PRIVATE_KEY }}
projectPath: './dist/build/mp-weixin'
错误监控与日志收集
实现前端错误收集:
// 错误处理拦截器
uni.addInterceptor('request', {
fail: (err) => {
this.logError({
type: 'request_error',
message: err.errMsg,
url: err.config.url,
time: new Date().toISOString()
})
}
})
// Vue错误捕获
Vue.config.errorHandler = (err, vm, info) => {
this.logError({
type: 'vue_error',
message: err.message,
component: vm.$options.name,
info,
stack: err.stack,
time: new Date().toISOString()
})
}
// 全局错误捕获
window.onerror = function(message, source, lineno, colno, error) {
logError({
type: 'window_error',
message,
source,
lineno,
colno,
stack: error && error.stack,
time: new Date().toISOString()
})
}
// 日志上报方法
logError(data) {
uni.request({
url: '/api/logs/error',
method: 'POST',
data,
header: {
'content-type': 'application/json'
}
})
}
国际化实现方案
使用vue-i18n实现多语言支持:
// 在main.js中配置
import Vue from 'vue'
import VueI18n from 'vue-i18n'
Vue.use(VueI18n)
const i18n = new VueI18n({
locale: uni.getLocale(), // 获取系统当前语言
messages: {
'zh-CN': {
home: '首页',
cart: '购物车',
product: {
price: '价格',
addToCart: '加入购物车'
}
},
'en-US': {
home: 'Home',
cart: 'Cart',
product: {
price: 'Price',
addToCart: 'Add to Cart'
}
}
}
})
// 在页面中使用
<template>
<view>
<text>{{ $t('home') }}</text>
<text>{{ $t('product.price') }}: ¥{{product.price}}</text>
<button @click="addToCart">{{ $t('product.addToCart') }}</button>
</view>
</template>
主题切换功能
实现动态主题切换:
// theme.js
const themes = {
default: {
primaryColor: '#ff0000',
secondaryColor: '#00ff00',
textColor: '#333333'
},
dark: {
primaryColor: '#990000',
secondaryColor: '#009900',
textColor: '#ffffff'
}
}
export function getTheme(themeName) {
return themes[themeName] || themes.default
}
// 在Vue中应用主题
computed: {
themeStyle() {
return {
'--primary-color': this.theme.primaryColor,
'--secondary-color': this.theme.secondaryColor,
'--text-color': this.theme.textColor
}
}
}
// 在模板中使用
<view :style="themeStyle" class="theme-container">
<!-- 内容 -->
</view>
// CSS中使用变量
.theme-container {
color: var(--text-color);
background-color: var(--secondary-color);
}
.button {
background-color: var(--primary-color);
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益,请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:跨端调试与问题排查
下一篇:实现一个多端社交 App