Develop a cross-platform e-commerce application
Cross-platform E-commerce Application Development Background
The development of e-commerce applications requires consideration of multi-platform adaptation issues. Traditional development methods require separate development for iOS, Android, Web, and other platforms, resulting in high costs and maintenance difficulties. uni-app, as a cross-platform framework based on Vue.js, allows for one-time development and simultaneous release to multiple platforms, significantly improving development efficiency. A typical e-commerce application usually includes core functional modules such as product display, shopping cart, order management, and payment.
Project Initialization and Configuration
When creating a uni-app project using HBuilderX, selecting the default template can quickly set up the basic structure. The project directory includes pages
for page files, static
for static resources, and App.vue
as the application entry file. The manifest.json
file configures basic information such as the application name and icon.
// Example manifest.json configuration
{
"name": "E-commerce Store",
"appid": "",
"description": "",
"versionName": "1.0.0",
"versionCode": "100",
"transformPx": false,
"app-plus": {
"splashscreen": {
"alwaysShowBeforeRender": true,
"autoclose": true,
"delay": 0
}
}
}
Core Page Development
Homepage Development
The homepage typically includes modules such as a carousel, product categories, and recommended products. The swiper
component in uni-app is used to implement the carousel effect:
<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: 'Phones', icon: '/static/phone.png'},
{id: 2, name: 'Computers', icon: '/static/computer.png'}
]
}
},
methods: {
navToCategory(id) {
uni.navigateTo({
url: `/pages/category/list?id=${id}`
})
}
}
}
</script>
Product Detail Page
The product detail page needs to display product images, prices, specifications, reviews, and other information. The uni-popup
component is used to implement the specification selection 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">Sold {{goods.sales}} items</view>
</view>
<view class="spec-section" @click="showSpecPopup">
<text>Select Specifications</text>
<uni-icons type="arrowright"></uni-icons>
</view>
<uni-popup ref="specPopup" type="bottom">
<view class="spec-popup">
<!-- Specification selection content -->
</view>
</uni-popup>
</view>
</template>
Shopping Cart Functionality Implementation
The shopping cart needs to manage product selection, quantity modification, and select-all interactions. Vuex is used for state management:
// 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
}
Shopping cart page implementation:
<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">Select All</checkbox>
<view class="total">
Total: ¥{{totalPrice}}
</view>
<button @click="checkout">Checkout ({{selectedCount}})</button>
</view>
</view>
</template>
Order and Payment Process
Order confirmation page displays shipping address, product list, and discount information:
<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>Please select a shipping address</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>Total: ¥{{totalAmount}}</text>
<button @click="createOrder">Place Order</button>
</view>
</view>
</template>
Payment process handling:
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: 'Failed to create order', icon: 'none' })
}
},
payOrder(orderId) {
uni.requestPayment({
provider: 'wxpay',
orderInfo: { orderId },
success: () => {
uni.redirectTo({
url: `/pages/order/result?orderId=${orderId}`
})
},
fail: (err) => {
console.error('Payment failed', err)
}
})
}
}
Multi-platform Adaptation Handling
Different platforms require handling style and functionality differences:
/* Conditional compilation for platform differences */
/* #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 */
Platform-specific API call example:
// WeChat Mini Program user info retrieval
// #ifdef MP-WEIXIN
uni.getUserProfile({
desc: 'For member profile completion',
success: (res) => {
this.userInfo = res.userInfo
}
})
// #endif
// Native functionality in APP
// #ifdef APP-PLUS
plus.share.sendWithSystem({
content: 'Share content',
href: 'https://example.com'
})
// #endif
Performance Optimization Strategies
- Image lazy loading:
<image lazy-load :src="item.image" mode="aspectFill"></image>
- Paginated product list loading:
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
}
- Using subpackage loading:
// pages.json
{
"subPackages": [
{
"root": "subpackageA",
"pages": [
{
"path": "goods/list",
"style": {}
}
]
}
]
}
Data Caching Strategy
Using uni-app's storage API for data caching:
// Retrieve cached data
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
}
}
// Update cache
async updateCache(data) {
await uni.setStorage({
key: 'goodsCache',
data: {
data,
timestamp: Date.now()
}
})
}
User Feedback and Review System
Product review component implementation:
<template>
<view class="review-section">
<view class="review-header">
<text>Product Reviews ({{reviews.length}})</text>
<text @click="showAllReviews">View All</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>
Real-time Communication Functionality
Integrating WebSocket for customer service:
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 = ''
}
})
}
}
}
}
Data Analysis and Monitoring
Integrating analytics SDK example:
// Initialize analytics in App.vue
export default {
onLaunch() {
// #ifdef APP-PLUS
const umeng = uni.requireNativePlugin('UMeng-Analytics')
umeng.init('your_app_key')
// #endif
// #ifdef H5
// Initialize Baidu Analytics
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
}
}
}
Continuous Integration and Deployment
Configuring GitHub Actions for automated deployment:
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: 'Automated deployment'
privateKey: ${{ secrets.MINI_PROGRAM_PRIVATE_KEY }}
projectPath: './dist/build/mp-weixin'
Error Monitoring and Log Collection
Implementing frontend error collection:
// Error handling interceptor
uni.addInterceptor('request', {
fail: (err) => {
this.logError({
type: 'request_error',
message: err.errMsg,
url: err.config.url,
time: new Date().toISOString()
})
}
})
// Vue error capture
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()
})
}
// Global error capture
window.onerror = function(message, source, lineno, colno, error) {
logError({
type: 'window_error',
message,
source,
lineno,
colno,
stack: error && error.stack,
time: new Date().toISOString()
})
}
// Log reporting method
logError(data) {
uni.request({
url: '/api/logs/error',
method: 'POST',
data,
header: {
'content-type': 'application/json'
}
})
}
Internationalization Implementation
Using vue-i18n for multilingual support:
// Configuration in main.js
import Vue from 'vue'
import VueI18n from 'vue-i18n'
Vue.use(VueI18n)
const i18n = new VueI18n({
locale: uni.getLocale(), // Get system current language
messages: {
'zh-CN': {
home: 'Home',
cart: 'Cart',
product: {
price: 'Price',
addToCart: 'Add to Cart'
}
},
'en-US': {
home: 'Home',
cart: 'Cart',
product: {
price: 'Price',
addToCart: 'Add to Cart'
}
}
}
})
// Usage in pages
<template>
<view>
<text>{{ $t('home') }}</text>
<text>{{ $t('product.price') }}: ¥{{product.price}}</text>
<button @click="addToCart">{{ $t('product.addToCart') }}</button>
</view>
</template>
Theme Switching Functionality
Implementing dynamic theme switching:
// 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
}
// Applying theme in Vue
computed: {
themeStyle() {
return {
'--primary-color': this.theme.primaryColor,
'--secondary-color': this.theme.secondaryColor,
'--text-color': this.theme.textColor
}
}
}
// Usage in template
<view :style="themeStyle" class="theme-container">
<!-- Content -->
</view>
// Using variables in CSS
.theme-container {
color: var(--text-color);
background-color: var(--secondary-color);
}
.button {
background-color: var(--primary-color);
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:跨端调试与问题排查
下一篇:实现一个多端社交 App