uni-app跨平台开发实战:一套代码,发布6平台 一、为什么选择uni-app在移动互联网时代一个产品往往需要同时覆盖Android、iOS、H5、以及微信/支付宝/百度等多个小程序平台。传统开发模式下每个平台都需要独立开发团队成本高、周期长、维护难。uni-app是DCloud公司基于Vue.js开发的跨端框架一套代码可以编译发布到iOS、Android、H5、以及各类小程序微信/支付宝/百度/字节/QQ/快手真正做到“Write once, run anywhere”。核心优势维度传统多端开发uni-app团队配置每个平台2-3人1个前端团队代码复用率低于20%80%以上开发周期3-6个月1-2个月维护成本多套代码并行一套代码统一维护技术原理uni-app 使用Vue.js 语法规范Weex 渲染引擎小程序组件化的混合架构开发者编写.vue文件编译时根据pages.json和manifest.json配置按目标平台输出对应的代码H5、小程序、原生应用二、环境搭建与项目创建2.1 安装脚手架bash复制下载# 安装vue-cli npm install -g vue/cli # 创建uni-app项目选择默认模板或TypeScript模板 vue create -p dcloudio/uni-preset-vue my-project # 进入项目目录 cd my-project # 安装依赖 npm install2.2 运行到不同平台bash复制下载# 运行到H5浏览器自动打开 npm run dev:h5 # 运行到微信小程序需先打开微信开发者工具并导入unpackage目录 npm run dev:mp-weixin # 运行到支付宝小程序 npm run dev:mp-alipay # 运行到百度小程序 npm run dev:mp-baidu2.3 使用HBuilderX推荐HBuilderX是uni-app官方IDE提供可视化创建、一键真机调试、云端打包等功能下载HBuilderX官网版本文件 → 新建 → 项目 → uni-app运行 → 运行到浏览器/手机/小程序模拟器三、项目结构与核心配置3.1 目录结构详解my-project/ ├── pages/ # 页面目录每个.vue文件自动生成对应页面 │ ├── index/ │ │ └── index.vue # 首页 │ └── user/ │ └── user.vue # 用户中心页 ├── components/ # 公共组件目录 ├── static/ # 静态资源目录图片/字体等不参与编译 ├── unpackage/ # 构建输出目录默认git忽略 ├── App.vue # 应用主入口生命周期/全局样式 ├── pages.json # 全局路由配置路由/TabBar/窗口样式 ├── manifest.json # 应用配置AppID/权限/平台特性 ├── uni.scss # 全局样式变量所有页面可引用 └── main.js # 入口文件Vue挂载/插件引入3.2 pages.json 完整配置{ pages: [ { path: pages/index/index, style: { navigationBarTitleText: 首页, navigationBarBackgroundColor: #007AFF, enablePullDownRefresh: true } }, { path: pages/detail/detail, style: { navigationBarTitleText: 详情页 } } ], globalStyle: { navigationBarTextStyle: white, navigationBarBackgroundColor: #007AFF, backgroundColor: #F8F8F8 }, tabBar: { color: #999999, selectedColor: #007AFF, backgroundColor: #FFFFFF, list: [ { pagePath: pages/index/index, text: 首页, iconPath: static/home.png, selectedIconPath: static/home-active.png }, { pagePath: pages/user/user, text: 我的, iconPath: static/user.png, selectedIconPath: static/user-active.png } ] }, condition: { current: 0, list: [ { name: 详情页调试, path: pages/detail/detail, query: id1001 } ] } }四、完整实战代码4.1 新闻列表页面完整功能template view classcontainer !-- 轮播图区域 -- swiper :indicator-dotstrue :autoplaytrue :interval3000 classbanner swiper-item v-for(item, idx) in banners :keyidx image :srcitem.image modeaspectFill classbanner-img / /swiper-item /swiper !-- 分类导航 -- scroll-view scroll-x classcategory-scroll view v-for(cat, idx) in categories :keyidx :class[category-item, { active: currentCategory cat.id }] clickswitchCategory(cat.id) {{ cat.name }} /view /scroll-view !-- 新闻列表 -- view classnews-list view v-foritem in newsList :keyitem.id classnews-item clickgoDetail(item.id) image :srcitem.cover lazy-load modeaspectFill classnews-cover / view classnews-info text classtitle{{ item.title }}/text view classmeta text{{ item.author }}/text text{{ formatTime(item.createTime) }}/text text{{ item.viewCount }}阅读/text /view /view /view /view !-- 加载状态 -- view classload-more v-ifisLoading uni-load-more :statusloadingStatus / /view !-- 空状态提示 -- view classempty v-if!isLoading !newsList.length image src/static/empty.png modeaspectFit / text暂无数据/text /view /view /template script import { formatTime } from /utils/date import { getNewsList, getBanners } from /api/news export default { data() { return { banners: [], categories: [ { id: 1, name: 推荐 }, { id: 2, name: 最新 }, { id: 3, name: 热点 } ], currentCategory: 1, newsList: [], pageNum: 1, pageSize: 15, hasMore: true, isLoading: false, loadingStatus: loading } }, onLoad() { this.init() }, onReachBottom() { if (this.hasMore !this.isLoading) { this.pageNum this.fetchNews() } }, onPullDownRefresh() { this.reset() this.init().then(() { uni.stopPullDownRefresh() }) }, methods: { formatTime, async init() { uni.showLoading({ title: 加载中 }) await Promise.all([this.fetchBanners(), this.fetchNews()]) uni.hideLoading() }, reset() { this.pageNum 1 this.newsList [] this.hasMore true }, async fetchBanners() { try { const res await getBanners() this.banners res.data } catch (err) { console.error(获取轮播图失败, err) } }, async fetchNews() { this.isLoading true this.loadingStatus loading try { const res await getNewsList({ categoryId: this.currentCategory, pageNum: this.pageNum, pageSize: this.pageSize }) const { list, total } res.data this.newsList this.pageNum 1 ? list : [...this.newsList, ...list] this.hasMore this.newsList.length total this.loadingStatus this.hasMore ? more : noMore } catch (err) { uni.showToast({ title: 加载失败, icon: none }) this.loadingStatus error } finally { this.isLoading false } }, switchCategory(categoryId) { if (this.currentCategory categoryId) return this.currentCategory categoryId this.reset() this.fetchNews() }, goDetail(id) { uni.navigateTo({ url: /pages/detail/detail?id${id} }) } } } /script style langscss scoped .container { background: #f5f5f5; min-height: 100vh; } .banner { height: 360rpx; -img { width: 100%; height: 100%; } } .category-scroll { white-space: nowrap; background: white; padding: 20rpx 0; } .category-item { display: inline-block; padding: 10rpx 30rpx; margin: 0 10rpx; font-size: 28rpx; border-radius: 40rpx; background: #f0f0f0; .active { background: #007AFF; color: white; } } .news-item { display: flex; margin: 20rpx; padding: 24rpx; background: white; border-radius: 16rpx; box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05); } .news-cover { width: 200rpx; height: 140rpx; border-radius: 8rpx; margin-right: 20rpx; } .news-info { flex: 1; display: flex; flex-direction: column; justify-content: space-between; } .title { font-size: 32rpx; font-weight: bold; color: #333; line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } .meta { display: flex; gap: 20rpx; font-size: 24rpx; color: #999; } /style4.2 条件编译平台差异化template view !-- H5平台特有内容 -- !-- #ifdef H5 -- view classh5-only这是H5特有的内容/view !-- #endif -- !-- 微信小程序特有内容 -- !-- #ifdef MP-WEIXIN -- button open-typegetUserInfo getuserinfoonGetUserInfo微信授权登录/button !-- #endif -- !-- App平台特有内容 -- !-- #ifdef APP-PLUS -- button clickappLoginAPP一键登录/button !-- #endif -- /view /template script export default { methods: { // 微信小程序方法 // #ifdef MP-WEIXIN onGetUserInfo(e) { if (e.detail.userInfo) { uni.setStorageSync(userInfo, e.detail.userInfo) } }, // #endif // App方法 // #ifdef APP-PLUS appLogin() { plus.oauth.getServices(services { // 苹果/安卓原生登录处理 }) } // #endif } } /script style /* H5平台特有样式 */ /* #ifdef H5 */ .h5-only { background: yellow; } /* #endif */ /* 微信小程序特有样式 */ /* #ifdef MP-WEIXIN */ /* 微信小程序样式 */ /* #endif */ /style4.3 封装请求拦截器// utils/request.js const BASE_URL https://api.example.com; const request (options) { return new Promise((resolve, reject) { const token uni.getStorageSync(token); uni.request({ url: BASE_URL options.url, method: options.method || GET, data: options.data || {}, header: { Content-Type: application/json, Authorization: token ? Bearer ${token} : }, success: (res) { if (res.statusCode 200) { if (res.data.code 401) { // 未授权跳转登录页 uni.reLaunch({ url: /pages/login/login }); reject(res.data); } else { resolve(res.data); } } else { reject(res); } }, fail: reject }); }); }; // 使用示例 // const res await request({ // url: /news/list, // data: { page: 1 } // });五、发布打包流程5.1 H5发布npm run build:h5 # 编译结果输出至 unpackage/dist/build/h5 目录 # 可将生成的静态资源部署至Nginx服务器或OSS存储5.2 微信小程序发布# 构建微信小程序生产环境代码 npm run build:mp-weixin # 使用微信开发者工具打开构建目录 open unpackage/dist/build/mp-weixin # 上传并提交审核代码5.3 原生App发布HBuilderX → 发行 → 原生App-云打包选择Android/iOS证书等待云端编译 → 下载安装包六、常见踩坑与解决方案问题原因解决方案图片不显示路径问题或大小超限使用/static/绝对路径小程序图片≤2M页面栈溢出频繁navigateTo超过10层使用uni.switchTab或uni.reLaunch样式不生效小程序不支持某些CSS使用display: flexposition替代复杂布局跨域请求失败微信小程序限制配置合法域名或使用云函数转发条件编译无效注释格式错误必须使用#ifdef#endif且无空格七、学习路径与资源推荐学习顺序Vue.js基础指令、组件、生命周期、Vuexuni-app官方文档重点pages.json、条件编译、组件仿写实战项目新闻资讯、电商、社交多平台测试真机调试、不同分辨率适配性能优化分包加载、图片懒加载、骨架屏官方资源官网https://uniapp.dcloud.io插件市场https://ext.dcloud.net.cn案例展示uni-app官网八、总结uni-app的核心价值效率提升一套代码覆盖6平台开发效率提升3-5倍生态丰富插件市场有大量现成组件开箱即用学习成本低基于Vue语法前端开发者快速上手社区活跃文档完善遇到问题容易找到解决方案适合场景中小型创业公司快速MVP验证企业内外部管理工具内容型、工具型小程序/H5应用不适合场景对性能要求极高的3D游戏需要大量原生交互的复杂应用一句话总结学会uni-app你就拿到了通往全栈跨端开发的入场券。一套代码多端覆盖这就是未来前端开发的效率之道。