从零开始:UniApp 跨平台开发实战与避坑指南
在跨平台开发领域,UniApp 凭借其“一套代码,多端发布”的能力,已成为众多开发者的首选方案。本文将结合实战经验,分享 UniApp 的核心开发技巧与常见问题解决方案。
一、为什么选择 UniApp?
UniApp 基于 Vue.js 语法,支持编译到 iOS、Android、H5、以及各类小程序平台。它的核心优势包括:
- 高效开发:一份代码,多端复用
- 性能接近原生:经过编译优化,体验优于传统 WebView 方案
- 插件生态丰富:DCloud 插件市场提供大量现成组件和模块
- 社区活跃:遇到问题更容易找到解决方案
二、项目初始化与目录规划
2.1 创建项目
使用官方 CLI 创建项目(推荐,更灵活):
npx degit dcloudio/uni-preset-vue#vite my-uni-app
cd my-uni-app
npm install
2.2 推荐的目录结构
src/
├── pages/ # 页面目录
├── components/ # 公共组件
├── static/ # 静态资源
├── utils/ # 工具函数
├── api/ # 接口封装
├── store/ # 状态管理(Vuex/Pinia)
├── styles/ # 全局样式
├── manifest.json # 应用配置
├── pages.json # 页面路由配置
└── App.vue # 应用入口
三、核心开发实践
3.1 条件编译处理多端差异
UniApp 最强大的特性之一就是条件编译,可以针对不同平台编写特定代码:
<template>
<view>
<!-- #ifdef MP-WEIXIN -->
<button open-type="getUserInfo">微信登录</button>
<!-- #endif -->
<!-- #ifdef APP-PLUS -->
<button @click="appLogin">App登录</button>
<!-- #endif -->
</view>
</template>
<script>
export default {
methods: {
getSystemInfo() {
// #ifdef APP-PLUS
plus.device.getInfo((info) => {
console.log('App设备信息:', info)
})
// #endif
// #ifdef MP-WEIXIN
wx.getSystemInfo({
success: (res) => console.log('微信设备信息:', res)
})
// #endif
}
}
}
</script>
<style>
/* #ifdef H5 */
.container {
width: 100vw;
}
/* #endif */
/* #ifdef APP-PLUS */
.container {
width: 100%;
}
/* #endif */
</style>
3.2 路由与页面跳转
UniApp 使用 pages.json 统一管理路由:
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页",
"enablePullDownRefresh": true
}
},
{
"path": "pages/detail/detail",
"style": {
"navigationBarTitleText": "详情页"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#FFFFFF"
}
}
页面跳转方法:
// 普通跳转(保留当前页面)
uni.navigateTo({
url: '/pages/detail/detail?id=123'
})
// 重定向(关闭当前页面)
uni.redirectTo({
url: '/pages/login/login'
})
// Tab 切换
uni.switchTab({
url: '/pages/user/user'
})
// 返回上一页
uni.navigateBack({
delta: 1
})
3.3 网络请求封装
建议对 uni.request 进行统一封装:
// utils/request.js
const BASE_URL = 'https://api.example.com'
const request = (options) => {
return new Promise((resolve, reject) => {
uni.showLoading({ title: '加载中...' })
uni.request({
url: BASE_URL + options.url,
method: options.method || 'GET',
data: options.data || {},
header: {
'Content-Type': 'application/json',
'Authorization': uni.getStorageSync('token') || ''
},
success: (res) => {
if (res.statusCode === 200) {
resolve(res.data)
} else if (res.statusCode === 401) {
// token 失效,跳转登录
uni.navigateTo({ url: '/pages/login/login' })
reject(res)
} else {
uni.showToast({
title: res.data.message || '请求失败',
icon: 'none'
})
reject(res)
}
},
fail: (err) => {
uni.showToast({
title: '网络异常,请稍后重试',
icon: 'none'
})
reject(err)
},
complete: () => {
uni.hideLoading()
}
})
})
}
export default request
3.4 状态管理(Pinia)
UniApp 官方推荐使用 Pinia 替代 Vuex:
// store/user.js
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
token: '',
userInfo: null
}),
getters: {
isLogin: (state) => !!state.token,
userName: (state) => state.userInfo?.nickName || '游客'
},
actions: {
setToken(token) {
this.token = token
uni.setStorageSync('token', token)
},
async login(phone, code) {
const res = await api.login({ phone, code })
this.setToken(res.token)
this.userInfo = res.userInfo
return res
},
logout() {
this.token = ''
this.userInfo = null
uni.removeStorageSync('token')
uni.clearStorageSync()
}
}
})
四、常见问题与解决方案
4.1 样式兼容性问题
问题:不同平台对 CSS 属性的支持程度不同。
解决方案:
/* 使用 rpx 作为基本单位,UniApp 会自动转换 */
.container {
width: 750rpx;
padding: 20rpx;
}
/* 针对特定平台覆盖样式 */
/* #ifdef H5 */
.container {
max-width: 750px;
margin: 0 auto;
}
/* #endif */
/* #ifdef APP-PLUS */
.container {
box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.1);
}
/* #endif */
4.2 图片处理
<template>
<view>
<!-- 网络图片需要配置域名白名单 -->
<image :src="imageUrl" mode="aspectFill"></image>
<!-- 静态图片推荐放在 static 目录 -->
<image src="/static/logo.png"></image>
<!-- 大图懒加载 -->
<image
v-for="(img, idx) in imageList"
:key="idx"
:src="img"
lazy-load
></image>
</view>
</template>
4.3 小程序端常见限制
// 1. 避免使用 eval、new Function 等动态执行代码
// ❌ 错误示例
const fn = new Function('a', 'b', 'return a + b')
// 2. 页面层级限制为 10 层,注意避免无限跳转
// 可使用 getCurrentPages() 检查当前页面栈
const pages = getCurrentPages()
if (pages.length >= 9) {
uni.redirectTo({ url: targetUrl }) // 使用 redirectTo 替代 navigateTo
}
// 3. 分包加载优化
// 在 pages.json 中配置分包
{
"subPackages": [{
"root": "packageA",
"pages": [
"pages/cat/cat",
"pages/dog/dog"
]
}]
}
4.4 性能优化建议
// 1. 使用 v-if 替代 v-show 减少渲染开销(小程序端)
// 2. 长列表使用虚拟滚动
// 推荐使用插件市场的 recycle-view 组件
// 3. 图片压缩与 WebP 格式
// 4. 减少页面 data 数据量
// 5. 避免在 onPageScroll 中执行复杂计算
// 6. 使用 uni.preloadPage 预加载
uni.preloadPage({
url: '/pages/detail/detail'
})
五、调试与发布
5.1 调试技巧
- H5 端:使用 Chrome DevTools
- App 端:使用 uni-app 调试器 + Android Studio / Xcode
- 小程序端:使用对应平台的开发者工具
5.2 发布命令
# 发布到 H5
npm run build:h5
# 发布到微信小程序
npm run build:mp-weixin
# 发布到 App(需要 HBuilderX 云打包)
# 或使用命令行打包
npm run build:app
六、写在最后
UniApp 虽然简化了跨平台开发,但仍需理解各平台特性差异。建议在开发初期就确定目标平台,合理使用条件编译,并时刻关注性能问题。
如果你正在开始一个新的跨平台项目,UniApp 绝对值得考虑。希望本文能帮助你少走弯路,顺利交付多端应用。
本文首发于技术博客,欢迎交流讨论。如有错误或补充,请留言指正。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END








暂无评论内容