免费资源下载
一、项目背景与架构设计
随着移动互联网的快速发展,企业需要同时覆盖iOS、Android、微信小程序、H5等多个平台。本文通过一个真实的跨境电商项目案例,展示如何使用UniApp构建高性能的跨端应用,并深度集成原生功能插件。
项目技术栈:
- 核心框架:UniApp 3.5 + Vue 3 + TypeScript
- UI框架:uView UI 3.0 + 自定义组件库
- 状态管理:Pinia 2.0 + 持久化存储
- 原生能力:Android/iOS原生插件 + 微信小程序插件
- 构建工具:Vite 4 + 自定义构建配置
项目架构图:
项目结构:
uni-app-project/
├── android-native/ # Android原生模块
├── ios-native/ # iOS原生模块
├── wx-plugin/ # 微信小程序插件
├── src/
│ ├── common/ # 公共资源
│ │ ├── api/ # 接口封装
│ │ ├── utils/ # 工具函数
│ │ └── constants/ # 常量定义
│ ├── components/ # 全局组件
│ │ ├── business/ # 业务组件
│ │ └── base/ # 基础组件
│ ├── pages/ # 页面文件
│ ├── static/ # 静态资源
│ ├── store/ # 状态管理
│ ├── uni_modules/ # 模块化插件
│ └── app.vue # 应用入口
├── vite.config.ts # Vite配置
├── manifest.json # 应用配置
└── package.json
二、性能优化深度实践
2.1 首屏加载优化方案
// src/common/utils/performance.ts
export class PerformanceOptimizer {
/**
* 图片懒加载优化
*/
static initLazyLoad() {
// 注册全局懒加载指令
uni.$app.directive('lazy', {
mounted(el: any, binding: any) {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target as HTMLImageElement
img.src = binding.value
observer.unobserve(img)
}
})
}, {
rootMargin: '50px',
threshold: 0.1
})
observer.observe(el)
}
})
}
/**
* 资源预加载
*/
static async preloadCriticalResources() {
const criticalResources = [
'/static/images/home-banner.jpg',
'/static/icons/essential.svg',
'/static/fonts/main.woff2'
]
// 使用Service Worker缓存关键资源
if ('serviceWorker' in navigator) {
const sw = await navigator.serviceWorker.ready
criticalResources.forEach(resource => {
sw.precache(resource)
})
}
// 预加载关键API数据
this.prefetchAPIData()
}
/**
* 分包加载策略
*/
static configureSubPackages() {
// manifest.json配置
const manifestConfig = {
"optimization": {
"subPackages": true
},
"subPackages": [
{
"root": "pages/product",
"pages": [
"detail/index",
"list/index",
"category/index"
]
},
{
"root": "pages/user",
"pages": [
"center/index",
"order/index",
"address/index"
]
}
]
}
// 动态分包加载
uni.loadSubPackage({
root: 'pages/product',
success: () => {
console.log('商品模块加载完成')
},
fail: (err) => {
console.error('分包加载失败:', err)
}
})
}
}
2.2 内存管理与渲染优化
// src/common/utils/memory-manager.ts
export class MemoryManager {
private static instance: MemoryManager
private cacheMap = new Map()
private maxCacheSize = 50
static getInstance(): MemoryManager {
if (!MemoryManager.instance) {
MemoryManager.instance = new MemoryManager()
}
return MemoryManager.instance
}
/**
* 图片内存缓存
*/
cacheImage(key: string, data: any): void {
if (this.cacheMap.size >= this.maxCacheSize) {
// LRU缓存淘汰策略
const firstKey = this.cacheMap.keys().next().value
this.cacheMap.delete(firstKey)
}
this.cacheMap.set(key, data)
}
/**
* 列表虚拟滚动
*/
static virtualScrollConfig = {
itemSize: 100, // 每个项目高度
bufferSize: 5, // 缓冲区大小
renderItem: (item: any, index: number) => {
return `
<view class="virtual-item" style="height: 100px;">
<text>${item.title}</text>
</view>
`
}
}
/**
* 大数据列表分片渲染
*/
static async renderLargeList(data: any[], container: any) {
const chunkSize = 20
const totalChunks = Math.ceil(data.length / chunkSize)
for (let i = 0; i {
requestAnimationFrame(() => {
this.renderChunk(chunk, container)
resolve(null)
})
})
// 给浏览器喘息时间
if (i % 5 === 0) {
await new Promise(resolve => setTimeout(resolve, 0))
}
}
}
}
三、原生插件深度集成
3.1 自定义扫码插件开发
// android-native/ScanModule.java
package com.example.uniapp.scan;
import android.app.Activity;
import android.content.Intent;
import com.alibaba.fastjson.JSONObject;
import io.dcloud.feature.uniapp.annotation.UniJSMethod;
import io.dcloud.feature.uniapp.bridge.UniJSCallback;
import io.dcloud.feature.uniapp.common.UniModule;
public class ScanModule extends UniModule {
private static final int REQUEST_SCAN = 1001;
@UniJSMethod(uiThread = true)
public void startScan(JSONObject options, UniJSCallback callback) {
Activity activity = mUniSDKInstance.getContext();
Intent intent = new Intent(activity, ScanActivity.class);
// 配置扫码参数
if (options != null) {
intent.putExtra("scanType", options.getString("type"));
intent.putExtra("flash", options.getBoolean("flash"));
intent.putExtra("vibrate", options.getBoolean("vibrate"));
}
activity.startActivityForResult(intent, REQUEST_SCAN);
// 保存回调
this.scanCallback = callback;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_SCAN && scanCallback != null) {
if (resultCode == Activity.RESULT_OK) {
String result = data.getStringExtra("scan_result");
JSONObject res = new JSONObject();
res.put("code", 200);
res.put("data", result);
res.put("success", true);
scanCallback.invoke(res);
} else {
JSONObject res = new JSONObject();
res.put("code", 500);
res.put("msg", "扫码失败");
res.put("success", false);
scanCallback.invoke(res);
}
scanCallback = null;
}
}
}
// src/uni_modules/scan-plugin/index.js
export default class ScanPlugin {
static install(Vue) {
// 注册全局扫码方法
Vue.prototype.$scan = {
/**
* 高级扫码功能
* @param {Object} options 配置选项
*/
async scan(options = {}) {
return new Promise((resolve, reject) => {
const scanModule = uni.requireNativePlugin('ScanModule')
const defaultOptions = {
type: 'qr_code',
flash: false,
vibrate: true,
sound: true,
rect: {
x: 0.1,
y: 0.3,
width: 0.8,
height: 0.4
}
}
const mergedOptions = { ...defaultOptions, ...options }
scanModule.startScan(mergedOptions, (result) => {
if (result.success) {
resolve(result.data)
} else {
reject(new Error(result.msg))
}
})
})
},
/**
* 批量扫码
*/
async batchScan(count = 5) {
const results = []
for (let i = 0; i < count; i++) {
try {
const result = await this.scan()
results.push(result)
} catch (error) {
break
}
}
return results
}
}
}
}
3.2 支付插件封装
// src/common/api/payment.ts
export class PaymentManager {
private static instance: PaymentManager
private paymentHandlers = new Map()
static getInstance(): PaymentManager {
if (!PaymentManager.instance) {
PaymentManager.instance = new PaymentManager()
this.initPaymentHandlers()
}
return PaymentManager.instance
}
private static initPaymentHandlers() {
// 注册各平台支付处理器
const instance = PaymentManager.instance
// 微信支付
instance.registerHandler('wechat', async (order) => {
#ifdef MP-WEIXIN
return await this.wechatMiniProgramPay(order)
#endif
#ifdef APP-PLUS
return await this.wechatAppPay(order)
#endif
})
// 支付宝支付
instance.registerHandler('alipay', async (order) => {
#ifdef MP-ALIPAY
return await this.alipayMiniProgramPay(order)
#endif
#ifdef APP-PLUS
return await this.alipayAppPay(order)
#endif
})
// Apple Pay
instance.registerHandler('apple', async (order) => {
#ifdef APP-IOS
const applePayModule = uni.requireNativePlugin('ApplePayModule')
return await applePayModule.pay(order)
#endif
})
}
/**
* 统一支付接口
*/
async pay(orderInfo: any, platform: string = 'auto'): Promise {
try {
// 自动检测平台
if (platform === 'auto') {
platform = this.detectPlatform()
}
const handler = this.paymentHandlers.get(platform)
if (!handler) {
throw new Error(`不支持的支付平台: ${platform}`)
}
// 创建支付订单
const order = await this.createOrder(orderInfo)
// 执行支付
const result = await handler(order)
// 验证支付结果
await this.verifyPayment(result)
return {
success: true,
data: result,
message: '支付成功'
}
} catch (error) {
console.error('支付失败:', error)
return {
success: false,
error: error.message,
message: '支付失败'
}
}
}
/**
* 支付状态轮询
*/
async pollPaymentStatus(orderId: string, timeout: number = 30000): Promise {
return new Promise((resolve, reject) => {
const startTime = Date.now()
const pollInterval = 2000
const poll = async () => {
try {
const status = await this.checkOrderStatus(orderId)
if (status === 'paid') {
resolve({ success: true, status })
return
}
if (status === 'failed') {
reject(new Error('支付失败'))
return
}
if (Date.now() - startTime > timeout) {
reject(new Error('支付超时'))
return
}
setTimeout(poll, pollInterval)
} catch (error) {
reject(error)
}
}
poll()
})
}
}
四、高级状态管理方案
4.1 基于Pinia的模块化状态管理
// src/store/modules/cart.ts
import { defineStore } from 'pinia'
import { CartItem, CartState } from '@/types/cart'
export const useCartStore = defineStore('cart', {
state: (): CartState => ({
items: [],
selectedItems: [],
totalPrice: 0,
lastUpdated: null
}),
getters: {
itemCount: (state) => state.items.length,
selectedCount: (state) => state.selectedItems.length,
isEmpty: (state) => state.items.length === 0,
// 计算总价(带缓存)
computedTotalPrice: (state) => {
return state.items.reduce((total, item) => {
return total + (item.price * item.quantity)
}, 0)
},
// 分组商品
groupedItems: (state) => {
const groups = new Map()
state.items.forEach(item => {
const key = item.shopId
if (!groups.has(key)) {
groups.set(key, [])
}
groups.get(key).push(item)
})
return Array.from(groups.entries())
}
},
actions: {
// 添加商品(带防抖)
async addItem(item: CartItem, quantity: number = 1) {
const existingItem = this.items.find(i =>
i.id === item.id && i.skuId === item.skuId
)
if (existingItem) {
existingItem.quantity += quantity
} else {
this.items.push({
...item,
quantity,
addedAt: Date.now()
})
}
// 自动保存到本地存储
await this.persistCart()
// 更新统计信息
this.updateStatistics()
},
// 批量操作
batchUpdate(items: CartItem[]) {
// 使用事务确保一致性
this.$patch((state) => {
items.forEach(newItem => {
const index = state.items.findIndex(
item => item.id === newItem.id
)
if (index !== -1) {
state.items[index] = { ...state.items[index], ...newItem }
}
})
state.lastUpdated = Date.now()
})
},
// 持久化存储
async persistCart() {
try {
const data = {
items: this.items,
version: '1.0',
timestamp: Date.now()
}
await uni.setStorage({
key: 'cart_data',
data: JSON.stringify(data)
})
// 同步到服务端
if (this.shouldSyncToServer()) {
await this.syncToServer()
}
} catch (error) {
console.error('购物车持久化失败:', error)
}
},
// 恢复数据
async restoreCart() {
try {
const { data } = await uni.getStorage({ key: 'cart_data' })
if (data) {
const parsed = JSON.parse(data)
this.$patch({
items: parsed.items || [],
lastUpdated: parsed.timestamp
})
}
} catch (error) {
console.error('购物车恢复失败:', error)
}
}
},
// 持久化配置
persist: {
key: 'cart_store',
storage: {
getItem: (key) => uni.getStorageSync(key),
setItem: (key, value) => uni.setStorageSync(key, value),
removeItem: (key) => uni.removeStorageSync(key)
},
paths: ['items', 'selectedItems']
}
})
4.2 全局状态监听器
// src/store/plugins/state-observer.ts
export const createStateObserver = () => {
return (context: any) => {
const store = context.store
// 监听状态变化
store.$subscribe((mutation, state) => {
// 记录状态变更日志
logStateChange(mutation, state)
// 触发相关副作用
triggerSideEffects(mutation, state)
// 性能监控
monitorPerformance(mutation)
})
// 监听Action执行
store.$onAction(({
name, // action名称
store, // store实例
args, // 参数
after, // action执行后的钩子
onError // action错误的钩子
}) => {
const startTime = Date.now()
// Action开始执行
console.log(`Action "${name}" started with args:`, args)
after((result) => {
const duration = Date.now() - startTime
console.log(`Action "${name}" finished in ${duration}ms`)
// 慢Action警告
if (duration > 1000) {
console.warn(`Action "${name}" took too long: ${duration}ms`)
}
})
onError((error) => {
console.error(`Action "${name}" failed:`, error)
// 错误上报
reportError(error, {
action: name,
args,
store: store.$id
})
})
})
}
}
// 状态变化日志
function logStateChange(mutation: any, state: any) {
const logEntry = {
type: 'STATE_CHANGE',
store: mutation.storeId,
mutation: mutation.type,
payload: mutation.payload,
timestamp: Date.now(),
stateSnapshot: deepClone(state)
}
// 保存到IndexedDB
saveToLogDB(logEntry)
// 开发环境控制台输出
if (process.env.NODE_ENV === 'development') {
console.log('State changed:', logEntry)
}
}
五、多端适配与兼容性处理
5.1 平台差异化处理
// src/common/utils/platform-adapter.ts
export class PlatformAdapter {
/**
* 获取平台特定配置
*/
static getPlatformConfig() {
const configs = {
// 微信小程序配置
'mp-weixin': {
navigationStyle: 'custom',
usingComponents: true,
permissionMap: {
camera: 'scope.camera',
location: 'scope.userLocation'
}
},
// 支付宝小程序配置
'mp-alipay': {
transparentTitle: 'auto',
titlePenetrate: 'YES',
permissionMap: {
camera: 'camera',
location: 'location'
}
},
// App配置
'app-plus': {
softinputMode: 'adjustResize',
splashscreen: {
autoclose: true,
waiting: true
}
},
// H5配置
'h5': {
routerMode: 'history',
template: 'h5',
devServer: {
port: 8080,
proxy: {}
}
}
}
const platform = this.getCurrentPlatform()
return configs[platform] || configs['h5']
}
/**
* 统一API调用
*/
static async unifiedAPI(apiName: string, params: any = {}) {
const platform = this.getCurrentPlatform()
switch (apiName) {
case 'chooseImage':
return await this.handleChooseImage(platform, params)
case 'getLocation':
return await this.handleGetLocation(platform, params)
case 'scanCode':
return await this.handleScanCode(platform, params)
default:
return await this.fallbackAPI(apiName, params)
}
}
/**
* 选择图片处理
*/
private static async handleChooseImage(platform: string, params: any) {
const defaultParams = {
count: 9,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera']
}
const mergedParams = { ...defaultParams, ...params }
#ifdef MP-WEIXIN
return await wx.chooseImage(mergedParams)
#endif
#ifdef MP-ALIPAY
return await my.chooseImage(mergedParams)
#endif
#ifdef APP-PLUS
return await uni.chooseImage(mergedParams)
#endif
#ifdef H5
return await this.h5ChooseImage(mergedParams)
#endif
}
/**
* H5端图片选择
*/
private static h5ChooseImage(params: any): Promise {
return new Promise((resolve, reject) => {
const input = document.createElement('input')
input.type = 'file'
input.accept = 'image/*'
input.multiple = params.count > 1
input.onchange = (e: any) => {
const files = Array.from(e.target.files)
const results = files.slice(0, params.count).map(file => {
return {
path: URL.createObjectURL(file),
size: file.size,
name: file.name,
type: file.type
}
})
resolve({
tempFilePaths: results.map(r => r.path),
tempFiles: results
})
}
input.onerror = reject
input.click()
})
}
/**
* 样式适配处理器
*/
static adaptStyle(styles: any): any {
const platform = this.getCurrentPlatform()
const adapted = { ...styles }
// 平台特定样式处理
switch (platform) {
case 'mp-weixin':
// 微信小程序样式适配
if (adapted.position === 'fixed') {
adapted.top = 'env(safe-area-inset-top)'
}
break
case 'app-plus':
// App样式适配
if (adapted.height) {
adapted.height = `calc(${adapted.height} + var(--status-bar-height))`
}
break
case 'h5':
// H5样式适配
if (adapted.cursor === 'pointer') {
adapted.userSelect = 'none'
}
break
}
return adapted
}
}
六、部署与监控体系
6.1 自动化构建部署
// vite.config.ts 多环境配置
import { defineConfig } from 'vite'
import uni from '@dcloudio/vite-plugin-uni'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
export default defineConfig(({ mode }) => {
const isProduction = mode === 'production'
const isDevelopment = mode === 'development'
return {
plugins: [
uni(),
createSvgIconsPlugin({
iconDirs: [path.resolve(process.cwd(), 'src/icons')],
symbolId: 'icon-[dir]-[name]'
})
],
// 构建配置
build: {
target: 'es2015',
minify: isProduction ? 'terser' : false,
terserOptions: {
compress: {
drop_console: isProduction,
drop_debugger: isProduction
}
},
rollupOptions: {
output: {
manualChunks: {
'vendor': ['vue', 'pinia', 'vue-router'],
'ui-library': ['uview-ui'],
'utils': ['lodash-es', 'dayjs']
},
chunkFileNames: 'assets/js/[name]-[hash].js',
entryFileNames: 'assets/js/[name]-[hash].js',
assetFileNames: 'assets/[ext]/[name]-[hash].[ext]'
}
},
sourcemap: !isProduction
},
// 开发服务器配置
server: {
host: '0.0.0.0',
port: 3000,
proxy: {
'/api': {
target: 'https://api.example.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^/api/, '')
}
},
hmr: {
overlay: false
}
},
// 环境变量
define: {
'process.env': {
NODE_ENV: JSON.stringify(mode),
APP_VERSION: JSON.stringify(process.env.npm_package_version),
BUILD_TIME: JSON.stringify(new Date().toISOString())
}
}
}
})
// package.json 构建脚本
{
"scripts": {
"dev:mp-weixin": "uni -p mp-weixin",
"dev:app": "uni -p app",
"dev:h5": "uni -p h5",
"build:mp-weixin": "uni build -p mp-weixin --mode production",
"build:app": "uni build -p app --mode production",
"build:h5": "uni build -p h5 --mode production",
"build:all": "npm run build:mp-weixin && npm run build:app && npm run build:h5",
"analyze": "uni build -p h5 --mode production --report"
}
}
6.2 应用监控与错误收集
// src/common/utils/monitor.ts
export class ApplicationMonitor {
private static instance: ApplicationMonitor
private performanceData: any[] = []
private errorLogs: any[] = []
static getInstance(): ApplicationMonitor {
if (!ApplicationMonitor.instance) {
ApplicationMonitor.instance = new ApplicationMonitor()
}
return ApplicationMonitor.instance
}
/**
* 初始化监控
*/
init() {
// 性能监控
this.setupPerformanceMonitoring()
// 错误监控
this.setupErrorHandling()
// 用户行为追踪
this.setupUserBehaviorTracking()
// 网络请求监控
this.setupNetworkMonitoring()
// 内存泄漏检测
this.setupMemoryLeakDetection()
}
/**
* 性能监控
*/
private setupPerformanceMonitoring() {
// 页面加载性能
uni.onAppShow(() => {
this.recordPerformance('app_show', Date.now())
})
// 路由性能
const originalNavigateTo = uni.navigateTo
uni.navigateTo = function(options: any) {
const startTime = Date.now()
const result = originalNavigateTo.call(this, options)
uni.$once('page_loaded', () => {
const loadTime = Date.now() - startTime
ApplicationMonitor.getInstance().recordPerformance(
'page_load',
loadTime,
{ url: options.url }
)
})
return result
}
// 自定义性能指标
this.monitorCustomMetrics()
}
/**
* 错误处理
*/
private setupErrorHandling() {
// 全局错误捕获
uni.onError((error: any) => {
this.recordError('global_error', error)
this.reportError(error)
})
// Promise错误
window.addEventListener('unhandledrejection', (event) => {
this.recordError('promise_error', event.reason)
this.reportError(event.reason)
})
// Vue错误
if (typeof Vue !== 'undefined') {
Vue.config.errorHandler = (err, vm, info) => {
this.recordError('vue_error', err, { vm, info })
this.reportError(err)
}
}
// 网络错误
uni.onNetworkStatusChange((res: any) => {
if (!res.isConnected) {
this.recordError('network_error', '网络连接断开')
}
})
}
/**
* 错误上报
*/
private async reportError(error: any, extra: any = {}) {
const errorData = {
type: error.name || 'UnknownError',
message: error.message,
stack: error.stack,
timestamp: Date.now(),
platform: uni.getSystemInfoSync().platform,
version: process.env.APP_VERSION,
page: getCurrentPages().pop()?.route || 'unknown',
userInfo: this.getUserInfo(),
extra
}
// 保存到本地
await this.saveErrorLog(errorData)
// 上报到服务器
if (this.shouldReportToServer()) {
await this.sendToErrorServer(errorData)
}
// 开发环境控制台输出
if (process.env.NODE_ENV === 'development') {
console.error('Captured error:', errorData)
}
}
/**
* 性能数据上报
*/
private async reportPerformance() {
if (this.performanceData.length === 0) return
const batchData = [...this.performanceData]
this.performanceData = []
try {
await uni.request({
url: '/api/monitor/performance',
method: 'POST',
data: {
data: batchData,
deviceInfo: uni.getSystemInfoSync(),
timestamp: Date.now()
},
header: {
'Content-Type': 'application/json'
}
})
} catch (error) {
// 上报失败,重新放入队列
this.performanceData.unshift(...batchData)
}
}
}
七、总结与最佳实践
7.1 关键技术总结
- 架构设计:采用分层架构,明确职责分离,提高代码可维护性
- 性能优化:从首屏加载、渲染性能、内存管理多维度优化
- 原生集成:通过原生插件扩展能力,保持跨端一致性
- 状态管理:使用Pinia实现响应式状态管理,支持持久化和模块化
- 多端适配:统一API封装,差异化处理各平台特性
7.2 开发规范建议
- 统一代码风格和命名规范
- 组件按功能模块划分,提高复用性
- API接口统一管理和错误处理
- 重要业务逻辑添加单元测试
- 定期进行代码审查和重构
7.3 持续优化方向
- 引入WebAssembly优化计算密集型任务
- 实现更智能的预加载和缓存策略
- 完善A/B测试和数据埋点体系
- 探索微前端在复杂应用中的应用
- 优化包体积,实现按需编译
7.4 注意事项
- 各平台审核规范差异需提前了解
- 隐私政策合规性需要重点关注
- 第三方服务稳定性要有降级方案
- 用户数据安全需要多重保障
- 版本更新需要考虑向后兼容

