
1. 三端并行发布的整体设计思路做跨端开发这几年uni-app 几乎成了我的默认起手式。不是说它没有坑而是对比下来它是目前唯一能让我用一个 Vue 语法栈同时交付 H5、微信小程序和原生 App 的方案而且踩坑资料多社区活跃遇到问题基本都能找到答案。这篇文章的核心是把“三端上线”这件事从开发完成之后开始讲起。很多人写 uni-app 教程重点都放在组件、API、条件编译上但真正到了要发布上线这一步反而容易卡住H5 端要不要配域名小程序备案备注信息怎么填App 云打包之后加固了怎么重新签名iOS 上架为什么总是被拒这些问题如果不提前规划项目开发完才发现返工成本极高。先说几个比较扎心的经验如果你正在规划一个 uni-app 项目请务必在开发前就确认H5 端必须有 HTTPS 域名不能用 IP 直连不能有端口号特殊情况除外否则微信内打开会直接被拦截。小程序端的 request 域名必须是备案过的 HTTPS 域名而且要在小程序后台配置白名单开发阶段可以勾选“不校验合法域名”但上线前必须配好。App 端如果涉及上架应用商店iOS 需要开发者账号个人 99 美元/年Android 需要准备软著、隐私政策等资质材料。这些不是发布当天才处理的事情。我在实际项目中通常会在项目启动第一周就把域名、备案、开发者账号全部申请到位因为备案审核周期有时长达 2-3 周App 软著申请也需要时间等到项目做完再准备上线时间至少延后一个月。另外还有个容易忽略的点三端的发布节奏是不一样的。H5 可以随时发布、随时回滚小程序需要经过微信审核通常 1-2 天加急能到几小时App 端 App Store 审核最不可控运气好 24 小时运气不好被驳回几次两星期都可能。所以我的项目管理习惯是H5 先上小程序紧随其后App 最后压轴这样即便 App 审核出问题业务也已经跑起来了。1.1 三端差异化需求对发布方案的影响uni-app 的三端说到底是三个不同的“壳子”H5 端运行在浏览器里受浏览器安全策略约束最典型的就是跨域问题和路由模式问题。小程序端运行在微信的 WebView 容器里受微信平台规则约束所有网络请求必须走合法域名包体积限制在 2MB主包分包总大小不超过 20MB。App 端运行在系统原生 WebView 或 uni-app 自带的渲染引擎里自由度最高但涉及原生权限、推送、版本更新、应用商店合规等问题。这些差异直接决定了发布配置的不同。我在项目里会在 manifest.json 里针对每个平台单独配置比如 H5 端的路由模式用 history小程序端不用管路由微信自己管理App 端则要额外配置模块权限。有一个很容易被忽略的问题H5 端的跨域问题。如果你的接口域名和 H5 部署域名不一致浏览器跨域请求就会被拦截。常见的解决方案有三种后端配置 CORS 允许跨域。开发环境用 HBuilderX 内置的代理或 vite 的 proxy 做转发。生产环境用 Nginx 反向代理比如 /api 路径转发到实际接口服务器。我见过不少团队在开发环境一切正常一部署到线上就白屏多半就是跨域没处理。关于跨域我强烈建议在项目初期就统一接口域名并且让后端配合配置好 CORS这样三端发布时能少掉一大半问题。小程序端对域名有强校验App 端没有域名限制但需要配置网络安全策略Android 9 默认禁止明文 HTTPH5 端受跨域限制三端对网络请求的约束各不相同这也是为什么网络请求的封装一定要在最开始就做好。1.2 三端上线前的账号与资质准备清单在上线流程开始之前所有账号和资质必须准备齐全。我整理了一个清单照着准备可以少走弯路端别必备账号/资质关键说明H5域名 HTTPS 证书 服务器域名需备案证书可用免费证书如 Lets Encrypt微信小程序微信小程序账号 小程序备案备案在微信公众平台操作需提供主体信息、负责人信息等App-iOSApple 开发者账号$99/年用于创建 App ID、证书、描述文件、上传到 App Store ConnectApp-Android软著证书 各商店开发者账号上架各安卓商店需软著部分商店可用电子版权替代关于小程序备案这是近两年新增的环节很多新手会卡在这一步。备案的核心是填写“小程序备注信息”这里我建议这样填一句话说明小程序的用途、服务对象和核心功能不要写模糊的“技术服务”“信息咨询”之类的词。比如一个小程序商城可以写“为个人用户提供在线购物服务涵盖商品浏览、下单支付、订单查询等功能”。如果你做的 App 涉及多个行业类目可能需要提供对应的资质文件比如食品类要食品经营许可证教育类要办学许可证金融类要金融牌照这些提前准备好。我还要特别提醒一下小程序备案审核期间小程序可以正常开发调试但不能发布上线。所以备案越早提交越好最好在项目开发初期就挂上等开发完备案也下来了直接就能提审不浪费时间。2. 环境准备与基础配置2.1 开发工具链选型和初始化uni-app 项目我从创建到发布默认使用 HBuilderX 作为主力工具。确实也可以用 Vue CLI 或 Vite 方式创建但 HBuilderX 的好处是与 uni-app 的云打包、真机运行、各端预览深度集成尤其是 App 端的云打包必须在 HBuilderX 里操作官方对这套流程的支持也最完善。HBuilderX 安装之后我建议开启这些设置文件 → 设置 → 编辑器配置 → 把 “保存时自动格式化” 打开。运行 → 运行到浏览器选择 Chrome方便 H5 端调试。运行 → 运行到手机或模拟器连接 Android 手机前开启 USB 调试。初始化项目时我习惯用uni-app 默认模板而不是空模板。默认模板内置了 pages.json、manifest.json、App.vue、main.js 这些基础结构还有 uni-ui 组件库的依赖省掉不少手动配置的时间。代码仓库我建议从一开始就用 Git并且设置好 .gitignore排除 node_modules、unpackage 这些编译产物。unpackage 目录是 HBuilderX 的编译输出目录体积很大而且每次都重新生成不需要提交到仓库。2.2 manifest.json 里的关键配置项manifest.json 是 uni-app 的灵魂配置文件所有端的关键配置都集中在这里。发布前需要重点核对这几个地方基础配置应用名称注意微信小程序端的名称以微信公众平台后台的名称和 AppID 为准App 显示名称则取这里的“应用名称”。AppID这里分两种情况。如果你是 HBuilderX 注册用户可以在manifest里重新获取 uni-app 官方的 AppID这是 DCloud 平台的标识如果你要跑微信小程序需要在微信公众平台申请自己的小程序 AppID然后在 manifest.json 的小程序配置里填这个 AppID。千万不要把两个 AppID 搞混填错会导致运行到微信开发者工具时报错。App 图标配置App 图标在很多平台的尺寸要求都不一样Android 端至少需要 192x192 的图标iOS 则需要 1024x1024 的图标。HBuilderX 的云打包界面可以自动生成各种尺寸的图标只需要准备一张 1024x1024 的源图即可这点比原生开发省力不少。App 模块配置如果你在 App 端要用到地图、推送、支付、分享、蓝牙等原生能力必须在 manifest.json 的“App 模块配置”里勾选对应模块。这里有个陷阱很多模块需要对应服务商的 SDK 配置比如支付模块需要配置微信支付的 appid、密钥等推送模块需要配置个推或厂商推送的密钥。这些配置如果没填打包出来的 App 运行到相应功能就会直接报错而且不是编译期报错是运行期才暴露。各端 SDK 配置在小程序配置里需要填微信小程序的 AppID在 App 的 SDK 配置里需要填微信登录、微信支付、支付宝支付等第三方平台申请的 key。这些 key 申请之后通常有审核周期要提前规划不要等到要打包了才去申请。配置完成后我习惯先跑一次H5 端确认页面正常再跑微信开发者工具确认小程序端最后再跑真机运行看 App 端。三端在开发期最好每周都过一遍不然积累到发布日再统一排查问题会被放大无数倍。3. H5 端发布实操从本地到线上3.1 H5 端编译配置与响应式适配H5 端发布前需要在 manifest.json 的 H5 配置里设置几个关键项路由模式默认是 hash 模式URL 里会带 #不太好看也不利于分享。线上建议改成 history 模式但 history 模式要求服务器必须配置单页应用回退否则用户在非首页刷新会 404。如果你用的是 Nginx需要加上 try_files 配置。运行端口本地开发默认 8080如果端口被占用可以在这里改。publicPath如果你的 H5 不是部署在域名根目录而是子目录比如 https://example.com/app/必须把 publicPath 设为/app/否则打包后资源路径会全部错乱页面白屏。这是我见过最多人踩的坑之一。关于 H5 响应式布局这是热词里出现频率很高的问题。uni-app 的 H5 端用的是 Vue Web 技术栈天然支持 CSS 媒体查询、Flexbox、Grid响应式布局本身不难做难的是设计稿到多端适配的思维转换。我的做法是以 750px 设计稿为基准用 rpx 作为单位小程序和 App 端用 rpx 能自适应。H5 端 rpx 也支持但要注意 rpx 在小程序里是 1rpx 屏幕宽度/750在 H5 端也是按屏幕宽度换算的所以两台不同宽度的手机看到的比例是等价的。但如果要做 PC 端的 H5建议在 PC 宽度超过某个阈值时启用单独的媒体查询限制最大宽度避免页面被拉得太宽。移动端适配的断点我一般分三档375px 以下小屏手机、375-768px手机到平板、768px 以上平板与桌面对应调整字体大小、间距和栅格列数。如果你不希望花太多时间在响应式上还有一个更省力的办法使用 uni-ui 的栅格组件。不过在我的实践里简单的 flex 布局加媒体查询已经能覆盖绝大多数场景不必过度设计。3.2 Nginx 部署与常见坑位H5 构建命令很简单在 HBuilderX 里点击“发行 → 网站-H5手机版”就会生成unpackage/dist/build/h5目录把这个目录里的所有文件上传到服务器即可。但这里有几个容易忽略的问题1. history 模式必须配置回退如果你的路由是 history 模式Nginx 配置必须是server { listen 443 ssl; server_name yourdomain.com; ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem; root /var/www/h5; index index.html; location / { try_files $uri $uri/ /index.html; } }关键是try_files $uri $uri/ /index.html;这行让所有前端路由都回退到 index.html由前端 router 接管否则用户在 https://yourdomain.com/home 刷新就会 404。2. gzip 压缩H5 的 js 和 css 文件体积不小开启 gzip 能显著提升加载速度gzip on; gzip_types text/plain text/css application/javascript application/json image/svgxml; gzip_min_length 1024;在 Chrome 开发者工具的 Network 面板里如果响应头里有content-encoding: gzip说明配置生效了。3. HTTPS 证书微信内打开的 H5 必须 HTTPS原因之一是微信内置浏览器会拦截非 HTTPS 请求。如果你还没有证书使用 Lets Encrypt 免费证书足够配合 certbot 自动续期工具几乎不需要人工干预。4. 缓存策略H5 的 index.html 建议设置no-cache保证每次发布后用户能拿到最新的页面入口而 js/css 文件因为文件名带 hash可以设置长期缓存location ~* \.(js|css|png|jpg|jpeg|gif|svg|woff2?)$ { expires 30d; add_header Cache-Control public, immutable; }这里要特别提醒一个发布问题如果你用了 history 模式并且 Nginx 没有配置好 try_files用户打开首页一切正常点击跳转也正常一旦刷新页面就 404。这个问题在项目上线第一天最容易出现因为测试人员通常不刷新页面直到真实用户反馈才暴露。所以部署完第一件事就是用无痕模式打开一级页面、二级页面然后强制刷新确认不 404。4. 小程序端发布实操备案、提审与上线4.1 微信小程序备案与提审小程序和 H5 最大的不同在于你写的代码需要经过微信官方审核才能上线。整个流程大致是注册小程序账号个人或企业主体。填写基本信息上传头像、简介等。完成小程序备案。开发完成后在微信开发者工具里上传代码填版本号和备注。登录微信公众平台提交审核。审核通过后点“发布”。备案这里我再多讲一点。小程序备案的备注信息很多人不知道怎么填我换个方式拆解如果你做的是商城类备注写“提供商品在线浏览、购买支付及订单查询服务”。如果你做的是工具类备注写“提供××方面的便捷查询与计算服务例如天气查询、日历查看等”。如果你做的是内容类备注写“提供××领域资讯/文章/视频的阅读与互动服务”。核心就一句话具体、清晰、不夸大。有些开发者贪多写一堆功能反而容易被要求补充材料。备案的审核标准是看你的服务是否合规、名称是否与实际功能一致写得越具体审核越顺畅。另外提审时有个容易被拒的细节小程序版本描述。微信审核人员只能通过你填写的版本描述来理解你的应用所以一定写清楚本次更新了什么功能、改了什么 Bug比如“新增用户积分功能修复商品详情页白屏问题”。如果项目里有涉及用户隐私的功能比如定位、通讯录、相册提审时还需要在后台配置对应的隐私接口说明微信有一份《小程序用户隐私保护指引》需要在后台填写“采集信息”和“用途说明”这些在提审前就要做好否则审核会被打回。4.2 小程序端代码上传与体验版管理在 HBuilderX 中点击“发行 → 小程序-微信”生成小程序代码后HBuilderX 会自动打开微信开发者工具。如果没自动打开可以手动导入unpackage/dist/dev/mp-weixin目录。上传代码时需要注意1. 微信开发者工具必须使用已绑定 AppID 的账号登录否则无法上传。登录后在详情里确认 AppID 和你申请的一致。2. 包体积控制微信小程序主包上限 2MB超过就要做分包。这也是 uni-app 的一个常见提醒。我在实际项目中会按照pages的业务模块进行分包比如商城类可以把商品列表、商品详情放到单独分包工具类可以把低频功能丢进分包这样可以明显降低启动体积。3. 体验版的管理上传代码后在微信公众平台 → 管理 → 版本管理 里把当前代码设为体验版。体验版需要添加体验成员成员扫码之后就可以看到这个小程序了。这一步很重要上线前让测试和产品先体验一轮发现小问题直接在后台改代码重新上传比上线后修复成本低很多。关于“小程序动态设置标题”热词里也提到了。这在 uni-app 里其实很简单用uni.setNavigationBarTitle({ title: 新标题 })就行。但要注意小程序端动态设置标题有尺寸限制最长 7 个汉字超过会被截断。另外有些页面如果你希望直接通过 URL 参数控制标题可以在 onLoad 里读取参数再调用这个 API。在页面 onShow 和 onLoad 里调用是有区别的onLoad只在首次加载时执行一次从别的页面返回再进这个页面时不会重新执行所以我一般放在onShow里做确保每次进入页面标题都是最新的。这里也顺便提一句小程序动态设置标题在实际业务里的典型场景最常见的是资讯类 App 的标题跟随文章标题变化还有电商类 App 的页面标题跟随分类变化。用 uni.setNavigationBarTitle 就够了不用改 pages.json 里的 navigationBarTitleText因为 pages.json 是静态的无法动态改。4. 域名白名单配置上线前必须在微信公众平台 → 开发管理 → 开发设置 → 服务器域名 里配置 request 合法域名、uploadFile 合法域名、downloadFile 合法域名。这一步非常重要小程序会强制校验不配置的话请求直接失败。开发阶段可以在微信开发者工具里勾选“不校验合法域名”但上线前一定要把域名配齐。5. 小程序的“体验版二维码”如果老板或客户需要在小程序上线前先看看效果传统的做法是把体验版二维码截图发给他。但体验版二维码有访问限制必须是已经添加的体验成员才能打开。所以记得提前把相关人员的微信都加到体验成员列表里。这个操作在后台添加后实际生效需要重新生成体验版二维码如果你已生成过二维码需要重新点一次“生成二维码”按钮让二维码更新为新成员可访问的状态否则别人扫码依然提示无权访问。最后一个容易被忽略的地方小程序会在审核通过后要求你选择“发布”或“暂不发布”。如果选择发布小程序立即上线如果选择暂不发布可以以后在版本列表里再点发布。我一般建议代码提审通过后先暂不着急发布可以自己在体验版上再快速过一遍关键流程确认无误再正式发布。特别是像微信支付这种涉及资金的功能出一丁点问题影响都很大。5. App 端发布实操云打包、加固、签名与上架5.1 uni-app 云打包与本地打包的选择App 端发布是三种端里最复杂的。uni-app 提供了两种打包方式1. 云打包HBuilderX 直接打包点击“发行 → 原生App-云打包”在弹窗里选择 Android 或 iOS勾选需要的渠道填好证书信息就可以打包。云打包的好处是不需要本地配原生环境缺点是排队时间不定高峰期可能要等很久。我建议避免在发布会前几小时才去云打包多留时间余量。2. 本地打包离线打包需要下载 Android Studio 或 Xcode配置原生工程把 uni-app 的资源包集成进去。这种方式适合需要高度定制原生功能或需要在本地做自动化打包的场景。缺点是环境配置比较繁琐官方文档虽然详细但初学者很容易在 Gradle 版本、SDK 版本这些问题上卡住。就我个人而言中小项目云打包完全够用。我遇到过必须本地打包的场景是多款 App 需要同一个 uni-app 核心工程然后各自定制不同的 App 名称和图标用云打包需要打很多次而本地打包可以写脚本批量处理。关于uni-app 打包 iOS 收不收费如果在 HBuilderX 里使用云打包 iOS标准打包是收费的费用和 DCloud 的开发者套餐相关。Android 云打包免费但如果你用的是 HBuilderX 标准基座或自定义基座有一些限制。如果你要做 iOS 打包我一般建议要么购买 DCloud 的套餐要么直接走本地打包用 Xcode 自身的能力来生成 ipa 文件这样不会受云打包的队列和时间限制。另外一个关键点iOS 打包必须有 Apple 开发者账号而且 if 要用 Xcode 打 ipa 包的话需要配置好对应的证书和描述文件。Apple 的证书机制比较复杂核心是你需要在 Apple Developer 后台生成证书签名请求CSR然后生成 .cer 证书再在后台生成 .mobileprovision 描述文件最后在钥匙串或 Xcode 里配置签名。简单说苹果通过双重机制来保证 App 的合法性一是代码签名证书二是描述文件包含设备 ID 和 App ID。5.2 Android 加固与重新签名操作Android 上架应用商店之前几乎所有主流商店都要求 App 进行安全加固。加固的本质是给 APK 加一层壳防止别人直接反编译查看代码逻辑常见的有腾讯乐固、360 加固、爱加密等。加固后的 APK 签名会失效因为加固厂商会重新签名一次但这个签名不是原开发者的签名所以应用商店无法校验必须重新签名。这就是热词里“uni-app 开发的 app 加固后如何重新签名”的痛点。我一般用apksigner工具来重新签名这是 Android SDK 自带的签名工具比 jarsigner 更推荐因为 Android 7.0 之后 apksigner 是官方推荐的签名方式。步骤大致是前提你要有原来的签名文件即 .jks 或 .keystore 文件以及对应的密码和别名。步骤一解压加固后的 APK加固工具会输出一个新的 APK。步骤二使用 apksigner 签名apksigner sign --ks your-release-key.jks --ks-key-alias your-alias --ks-pass pass:your-password --out signed-app.apk unsigned-app.apk步骤三验证签名apksigner verify signed-app.apk如果输出Verified using v1 scheme: true和Verified using v2 scheme: true说明签名成功。这里有几个坑要提醒jks 文件务必保存好丢了就没办法更新应用了只能换包名重新上架。签名文件不要提交到代码仓库也不要发给第三方签名泄露会导致别人利用你的签名发布恶意版本。不同商店要求不同有的是上市场时平台自动加固有的是要求你自行加固后上传要提前了解清楚目标商店的规范。5.3 iOS 上架与证书配置iOS 上架的流程比 Android 更严格。步骤大致是在 Apple Developer 后台注册 App ID。创建开发证书和发布证书。创建描述文件。在 Xcode 或 HBuilderX 中配置打包。上传到 App Store Connect。填好 App 信息名称、简介、截图、隐私政策、审核备注等。提交审核。常见被拒的理由App 功能与描述不符截图里展示的功能和实际不一致或者 App 里没有相关功能但仍然写了相关文案。缺乏隐私政策尤其是涉及用户数据采集的 App必须有完整的隐私政策链接。使用了私有 API也就是苹果不允许开发者调用的 API审核机器扫描到会直接拒绝。界面存在 bug 或崩溃审核人员实际运行你的 App如果某个页面白屏或点按钮无反应会被打回。没有提供测试账号如果你的 App 需要登录才能用一定在审核备注里提供可用的测试账号否则审核人员进不去等于是间接宣告审核失败。关于 App Store Connect 的截图要求iPhone 6.7 英寸或 6.5 英寸是必须的也就是对应最新大屏机型的截图。很多人第一次上架不知道截图尺寸怎么配去 App Store Connect 就能看到截图上传区域有明确标注尺寸要求按提示操作即可。还有一个常见疑问是App 名字和图标在上架审核时会不会不一致。会的如果你后台填的名字和用户手机上显示的名字不一致苹果可能会让你统一。所以我在提交前会再三确认三处名字一致manifest.json 里的应用名称、App Store Connect 里的 App 名称、手机上安装后的显示名称。5.4 App 端的渠道包分发Android 上架不是一个商店就完了国内主流商店有华为、小米、OPPO、vivo、应用宝、360 等每个商店都要上传。虽然很多商店支持自动填充但信息量大尤其是隐私政策、权限说明、应用截图这些每家都要单独准备一份。如果你不想一个一个手动上传有两个办法用 uni-app 的 uni-stat 统计各渠道数据在发行时勾选不同渠道。用 DCloud 的“分发”机制或第三方分发平台比如蒲公英、fir.im可以生成一个内测链接方便测试人员下载安装。内测分发不等于上架应用商店但开发阶段做真机测试是很好用的。另外App 正式打包上传到商店后商店审核周期一般是 1-3 个工作日也要预留时间。加急通道一般只在紧急情况下使用费用较高且并非所有应用都适用。6. 三端上线中的常见问题与排查技巧6.1 网络请求与域名配置问题“uni-app network: unavailable”这个热词在开发圈出现频率很高其实这是 DCloud 控制台的报错信息并不是你代码里的报错。它的准确含义是当前基座没有网络权限或者是你正在使用自定义基座但在 HBuilderX 里没有配置对应加密网络。如果遇到这个报错我通常按以下步骤排查检查手机系统是否给 App 开启了网络权限Android 在设置 → 应用 → 权限 → 网络iOS 在设置 → 蜂窝网络里查看。检查 manifest.json 里是否勾选了网络相关模块特别是如果你用了自定义基座。检查代码里请求的 URL 是否是 HTTPSAndroid 9.0 以上系统默认禁止明文 HTTP如果不是 HTTPS需要在 manifest 里开启网络安全配置。如果用的是本地开发服务器比如http://192.168.x.x:8080需要确认手机和电脑在同一局域网并且服务器允许局域网访问。还有一种场景是开发时好好的、发布后请求失败这种多半是域名问题。在小程序端要检查域名是否在后台配置、是否备案、是否支持 HTTPS在 App 端要检查是否配置了明文流量策略在 H5 端要检查是否是跨域。另外小程序里用 code 换 token也是很多人问的问题。微信小程序登录的流程是wx.login()拿到 code → 把 code 传给后端 → 后端用 code appid secret 换取 openid 和 session_key → 后端自己生成 token或者直接把 session_key 当 token返回给前端。前端把 token 存起来后续请求时带上。在 uni-app 里获取 code 用的是uni.login()而且注意在小程序端 uni.login 的成功回调里拿到的 code 是临时凭证5 分钟有效期只能使用一次。后端如果调用微信接口换 openid 失败大概率是 code 已过期或者是拿 appid/secret 配错了。在 H5 端 uni.login 是不生效的H5 端一般用账号密码或第三方 OAuth 登录条件编译要区分处理。如果你发现小程序里能登录H5 里登录没反应多半就是这个原因。6.2 样式与布局兼容问题三端样式不统一是开发中很常见的问题。我总结了几条实战规律1. 小程序的 button 有默认样式小程序里的button自带边框和背景色成人习惯在 H5 里用button自定义样式时在小程序里可能莫名其妙多出边框。解决方法是给 button 设置::after去掉边框或者直接用view模拟按钮。2. 小程序的 placeholder 样式H5 里设置input::placeholder在小程序端不生效小程序端要使用placeholder-class自定义类名。在 uni-app 里如果你写的是 vue 单文件可以直接给 input 加 class 属性配合placeholder-class属性来指定样式类。3. 安全区域适配iPhone X 及之后机型有底部 Home 指示条页面底部的内容可能会被遮挡。要处理这个App 端和小程序端可以使用env(safe-area-inset-bottom)或者在 uni-app 里直接用官方提供的padding-bottom: constant(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom);。H5 端在微信浏览器和普通浏览器里的表现也不一致建议底部操作栏统一加这个 padding。4. 字体大小适配小程序端自带的字体单位是 rpxH5 端可以用 rpx 也可以用 px。我的建议是保持用 rpx这样三端视觉大小几乎一致。如果某些地方必须用 px比如设计稿要求固定高度注意换算公式750rpx 屏幕宽度1px ≈ 2rpx在 375px 宽的屏幕上。还有一个经典的 H5 响应式问题图片宽度自适应。在 uni-app 中image组件默认有固定尺寸不会像img一样按父容器宽度自适应。如果你想要图片宽度 100%高度按比例缩放需要设置modewidthFix。这个属性很多新手不知道导致 H5 端图片显示变形。6.3 原生能力调用与 API 差异热词里有“uni-app ble ios 可以根据蓝牙的 deviceid 建立连接吗”这其实是个典型的 API 差异问题。答案是iOS 上不能直接用 deviceId 连接蓝牙设备。iOS 蓝牙的 deviceId 是系统随机分配的每次 App 启动后重新扫描得到的 deviceId 都可能变化所以不能用 deviceId 做持久化标识。iOS 上想稳定识别一个蓝牙设备一般是在设备的广播数据或服务数据里找设备的 MAC 地址或者设备自定义标识然后保存这个标识下次用这个标识去匹配。在 uni-app 里使用蓝牙 API 时要注意几个点uni.openBluetoothAdapter()必须先调用否则后续蓝牙操作会失败。uni.startBluetoothDevicesDiscovery()扫描到设备后回调里的deviceId在小程序端可能变化。连接设备uni.createBLEConnection()后uni.getBLEDeviceServices()获取服务uni.getBLEDeviceCharacteristics()获取特征值然后通过uni.writeBLECharacteristicValue()发送数据。iOS 端写数据时如果特征值不支持 write 只支持 writeWithoutResponse需要对应选择发送方式。直接用 write 方式却不支持会导致写数据回调失败。这个问题牵扯到蓝牙底层协议我建议在使用前先读一下 uni-app 官方文档里蓝牙 API 的说明并且实际在 iOS 和 Android 真机上各测一遍因为两者对于设备发现和连接的行为差异较大。除了蓝牙还有几个常见的三端差异uni.getSystemInfoSync()的返回值小程序端的statusBarHeight和 App 端可能不同实测为准。uni.setNavigationBarTitle()在 App 端可能需要配合plus原生 API 使用。uni.request()的响应体在小程序端返回的是一个对象H5 端如果你期望的是字符串可能需要手动转换。uni.navigateTo()的页面层级限制微信小程序最多 10 层超过后无法继续 navigateTo需要用uni.redirectTo()或uni.reLaunch()。App 端还有一个必须注意的问题权限申请时机。Android 在运行时需要动态申请权限比如定位、相机、相册。如果你在代码里直接调用相机但没申请权限或者用户拒绝了权限App 会直接崩溃或功能异常。在 uni-app 里你需要在小程序端配置权限描述在 App 端使用uni.authorize或原生插件申请权限。合理的方式是在需要用到权限的功能入口处弹窗而不是进入 App 时一次性申请所有权限这样转化率更高也更容易通过商店的合规审核。6.4 小程序端审核被退回的常见原因小程序审核其实是有一套相对固定的模式被退回多数是以下原因类目不符你的小程序实际功能和你选择的服务类目不匹配。比如你选了“教育”但实际做的是电商审核人员会直接拒绝。解决办法是选择准确的类目并提交对应的资质文件。内容不完整页面里有“待开发”“功能即将上线”这类占位文案或者空页面。提审前一定要把所有入口、空状态页面补上合理的提示。无法体验核心流程如果小程序需要登录审查员需要一个你可以提供的测试账号。如果不提供他们无法进入系统审核时长会被拉长甚至拒绝。涉及敏感行业未提交资质如医疗、金融、游戏等需要提前准备好相关资质。隐私政策缺失或不完整尤其是涉及用户个人信息的没有隐私弹窗、没有同意按钮审核必拒。我的经验是提交审核前自己先按“审核人员视角”走一遍。想象一个对你的业务完全不了解的用户打开小程序第一眼看到什么能不能理解这个 App 是干什么的最核心的功能入口是否直观。如果页面看不出用途文案含糊不清大概率会被打回。另外小程序审核通过后还有“用户隐私保护指引”检查。微信有时候会要求你补充隐私政策你需要在后台配置一份完整的隐私政策文本并确保代码里对用户信息的采集在同意之后才开始。6.5 H5 端移动端与桌面端适配策略H5 端发布后还要考虑一个问题用户可能在手机浏览器、Pad、PC 上都打开你的页面。如果业务需要做全端覆盖不能只盯着手机。我的做法是在App.vue里监听窗口宽度onLaunch() { // 判断设备类型 uni.getSystemInfo({ success(res) { if (res.windowWidth 768) { // 桌面宽度启用桌面端布局 } } }); }然后在样式层通过媒体查询设置最大宽度media screen and (min-width: 768px) { .container { max-width: 1200px; margin: 0 auto; } }如果你做的是管理后台类的 H5最好直接设计成桌面端优先表单、表格这些组件在宽屏下才好看。移动端优先的设计会导致 PC 上拉伸变形观感很差。7. 远程升级与后续维护建议7.1 uni-app 的远程升级方案App 上线后不可能一劳永逸尤其 Android 生态碎片化严重你不可能控制所有用户都更新到最新版。所以远程升级能力是必须的。热词里“uni-app 远程升级”也是高频问题。uni-app 的 App 远程升级有两种方式1. 整包升级原生 APK 升级用户在 App 内检测到新版本后提示下载新 APK 安装包安装后替换旧版本。这种方式适合大版本更新比如原生模块调整、SDK 升级等。在 uni-app 里可以通过uni.getUpdateManager()仅小程序端或原生插件实现App 端一般用plus.runtime.openURL或专门的升级插件实现下载安装。2. wgt 资源热更新uni-app 的 wgt 包是前端资源的增量包可以理解为 App 壳不变只更新里面的 JS 和页面资源。uni-app官方文档提供了uni.upgradeCenter之类的接口可以在线检测并下载 wgt 包。但要注意wgt 热更新不适合原生模块改变的场景比如你在 manifest 里新增了一个原生插件热更新是没办法加载新插件的必须重新打整包。我的建议是小 bug 修复、页面文案调整用 wgt 热更新原生插件变更、SDK 升级、重大 UI 重构用整包升级。同时在服务端维护一份版本信息表包含当前最新版本号、最低版本号、升级包 URL、更新说明等每次 App 启动时请求一次判断是否提示升级。7.2 三端上线后的数据监控与反馈闭环最后再说一点上线不是终点而是新的起点。三端上线后我每天固定做的事是早上看一遍三端的崩溃日志小程序在微信公众平台看App 用友盟或 uni-stat 看H5 用 Sentry 或阿里云日志。关注线上反馈渠道尤其是小程序端的用户投诉和评分。每周更新一次小程序版本如果有必要每次更新都要走提审流程。每两周发布一个 App 版本保持用户对 App 的活跃度感知。有一个小技巧与大家分享H5 端、小程序端、App 端的版本号要各自维护。比如小程序 v1.2.0、App v2.1.0不要试图统一因为三端的迭代节奏和审核机制都不一样。我在代码里会通过条件编译区分当前运行环境在“关于页面”里显示当前端的名称和版本号这样排查用户问题时可以迅速定位。另外配置一个比较完善的操作日志系统也很重要。三端共用一套后端 API如果你的 API 有版本管理客户端在请求时带上当前端类型和版本号后端就能识别出问题集中在哪一端。这个信息在联调时价值不大但在线上出问题时能帮你省掉大量猜测时间。三端上线这件事说复杂也复杂说简单也简单。核心就在于把每一端的账号、资质、配置提前准备好把每一端的审核规则摸透然后逐端的发布、验证、监控形成一个闭环。我踩过的那些坑——备案没提前做、iOS 证书配错、加固后忘签名、H5 history 路由没配回退——你现在提前看到了就能少走弯路的概率大很多。如果这篇文章能帮你顺利把三端都发布出去那我花时间写这些内容就值了。