ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

uni-app打包发布全流程详解:从三端差异到常见报错排查

uni-app打包发布全流程详解:从三端差异到常见报错排查 做uni-app开发的人十有八九会在打包发布这个环节栽过跟头。平时在H5浏览器里点得飞快一打包就各种问题冒出来——App连不上ws、小程序webview通信失败、云打包排队两小时、证书签名搞不清。这套流程说难不算难但坑确实多而且踩过的坑之间很有共性。这篇博文我就把uni-app打包与发布整个链路从头到尾捋一遍从三端产物的差异、微信小程序打包细节、App端APK和IPA的双端流程、离线打包和uts插件再到发布更新和常见报错排查全部用我实际跑过的项目经验来讲适合刚接触uni-app打包的新手也适合被各种奇奇怪怪报错折磨得想摔键盘的资深开发。1. 打包前先想清楚你的产物到底要跑在哪个平台上1.1 三端打包产物差异uni-app最常用的卖点就是一套代码多端发布但很多人忽略了一个事实一套代码不等于一个包。你最终打包出来的东西在H5、微信小程序、App三种平台上完全不是一个形态发布逻辑也完全不同。H5端的产物其实就是一堆静态文件index.html、js、css扔到nginx或者任意静态服务器上就能跑本质就是一个Web项目不存在什么安装包的概念。微信小程序端打包出来的是一套符合微信小程序规范的包结构里面有app.json、pages目录、各个组件的编译产物这个包不能直接运行必须上传到微信公众平台经过审核才能发布。App端则最复杂Android需要生成APK或AABiOS需要生成IPA这两个都是真正的安装包。而且App端除了js代码还有一个原生壳的问题——uni-app最终是把js bundle嵌进一个原生工程里运行的所以需要涉及签名、证书、原生SDK版本匹配这一大堆事。我做项目时最深的感受是很多人会把打包理解成点一下按钮就完事但实际上你在决定打包方式之前得先确认目标平台。如果你要同时发布三端建议分别走三套打包流程而不是指望一个配置全搞定。三端的代码可以复用百分之八十但打包这个环节每端都得单独伺候。1.2 云打包和本地打包到底怎么选uni-app的打包有两种主流方式云打包和本地打包。这两者的取舍很多人都没完全搞清楚导致做了一半才开始换方案非常浪费时间。云打包是在HBuilderX里配置好项目之后点菜单栏的发行选择相应平台然后DCloud的服务器帮你完成打包。这个方案最大的好处是你本地不需要装Android Studio、不需要配SDK、不需要装Xcode只要能联网就行。它的缺点也明显要排队高峰期等半小时到一小时很正常遇到打包失败只能看日志猜原因无法调试原生层如果你想自定义原生功能但不懂Android原生开发云打包基本只能靠插件市场现成的插件。本地打包则正好相反你需要去DCloud官网下载对应版本的离线SDKAndroid端用Android Studio打开工程iOS端在Mac上配合Xcode操作。这个方式的优势是完全可控可以自由编写原生代码和自定义模块调试方便构建速度快尤其适合企业内部分发、需要深度定制原生功能、或者需要接比较复杂的三方SDK的场景。缺点是环境搭起来费劲Android SDK、Gradle版本、各种依赖一不留神就冲突。我的实际建议是做原型验证、快速出包的时候用云打包省时省力做正式项目、尤其需要接入原生插件或者企业内部自定义功能的时候尽早转本地打包。很多团队等到开发中后期突然发现云打包满足不了原生需求再转离线打包项目结构都要调整那叫一个痛苦。2. 微信小程序端打包配置、webview通信和renderjs2.1 manifest配置和打包步骤微信小程序端的打包在HBuilderX里操作相对简单但配置项非常关键。首先你要在manifest.json里正确填入微信小程序的AppID。很多新手用测试号开发到发布时才发现测试号的AppID跟正式账号不一致会导致一系列奇怪的问题所以建议开发第一天就申请好正式的AppID填进去避免后续折腾。配置好之后点击菜单栏发行 - 小程序-微信HBuilderX就开始编译。编译完成后会在项目的unpackage/dist/dev/mp-weixin或unpackage/dist/build/mp-weixin目录下生成小程序代码。如果你是开发调试直接用微信开发者工具导入这个目录如果要发布就在微信开发者工具里点击上传然后去微信公众平台提交审核。打包过程中有几个细节值得注意。第一微信小程序主包体积限制是2MB以内超过之后必须用分包加载。确实超了也不慌在pages.json里配置subPackages字段把不是首屏必须的页面拆到分包里就行。第二如果项目里用了较高版本的ES语法开发者工具会提示做ES6转ES5别嫌麻烦直接勾选转换否则低版本微信客户端会出现白屏。第三我见过很多项目打包后出现check out of index或者Nopage found这类报错八成是pages.json里的页面路径配置有问题或者某些组件被tree-shaking掉了。2.2 微信小程序webview与H5页面的双向通信我在热搜里看到uni-app 微信小程序webview 如何像h5通信这个问题这确实是个高频需求。场景一般是小程序里某个页面用web-view组件嵌入了H5网站比如按个帮助文档、活动页或者复杂表单然后需要在小程序和H5之间传数据。先说小程序给H5传值。最直接的办法是拼URL参数web-view组件的src属性里直接带queryH5端通过URL解析就能拿到。如果你的参数比较敏感强烈建议在服务端生成一个一次性token拼在URL里H5再拿这个token去服务端换真实的用户身份避免把用户ID、手机号这种信息明文暴露在URL里。再说H5给小程序传值。在H5页面里如果运行环境是微信浏览器内嵌小程序web-view可以通过微信官方提供的jssdk接口来通信核心是wx.miniProgram.postMessage。这里有个非常容易踩的坑postMessage不是实时的它的消息会进入一个消息队列只有在特定时机才会被小程序端收到这些时机包括小程序页面被分享、用户点击右上角菜单转发、页面销毁比如bindmessage监听后返回等。也就是说你在H5里每次操作都立刻postMessage小程序端不可能每条都实时收到。所以设计通信逻辑时要把消息设计成最终状态而不是过程状态比如H5表单填写完成后在点击提交并返回小程序那一步一次性把最终结果post过去这样实际工程体验会好很多。小程序端监听消息是在web-view组件上绑定bindmessage事件事件对象里可以拿到e.detail.data数组里面就是H5通过postMessage传过来的消息。我遇到过不少开发者不知道bindmessage拿到的data是个数组直接当成对象用结果取不到值其实仔细看官方文档或者打个断点看一眼数据结构就明白。2.3 renderjs的正确用法和注意事项热搜里还有uni-app x 怎么使用renderjs这个我印象深刻因为renderjs这个技术点向来容易把人绕晕。先说明一下renderjs并不是uni-app所有平台都支持统一写法的功能它的核心用途是在视图层运行一段JavaScript可以操作DOM、访问window对象从而弥补逻辑层无法直接操作DOM的限制。在普通uni-app项目里renderjs的使用方式是在组件的script标签中加一句langrenderjs并设置module名。基本结构是这样的组件template里正常写界面一个script langrenderjs modulemyRender的节点用来写视图层代码再加一个普通script节点写逻辑层代码。视图层通过myRender.xxx()这种形式被逻辑层调用反过来视图层可以调用this.$emit(自定义事件名称, 参数)来告诉逻辑层发生了什么事情。这种模式在渲染echarts、map等需要大量DOM操作的组件时尤其好用避免逻辑层和视图层之间频繁通信导致卡顿。不过在uni-app x项目中情况会有变化。uni-app x走的是uvue语法它对renderjs的支持和旧版uni-app有所区别官方文档里明确了很多API只在特定平台可用。根据我个人项目里的经验如果在uni-app x里要复用旧版renderjs的写法经常会出现方法不存在或模块未找到这类问题。我的建议是如果你用的确实是比较新的uni-app x务必先去官方文档看当前版本对renderjs的支持说明不能用旧版文章里的代码直接copy。另外一个实践是如果你要做复杂图表或地图优先考虑用插件市场里为uni-app x专门适配过的组件省掉的改造成本绝对比你想象中多。3. App端打包从APK到IPA的完整流程3.1 Android云打包和证书生成Android端的打包我用云打包的次数最多整体流程其实可以概括为配置manifest - 生成证书 - 填证书签 - 点发行 - 下载APK。证书是很多第一次做App的人完全没概念的东西。Android要求每个安装包必须用数字证书签名这个证书就好比你的身份标识Android系统靠它来判断App是不是同一个开发者发布的。你在HBuilderX云打包界面上如果只用公共测试证书安装没问题但一旦想上架应用市场或者做版本更新就必须要自己有一个独立的证书。生成证书的常用工具是keytoolJDK自带命令行里敲一下就能生成我常用的指令是这样的keytool -genkey -alias myalias -keyalg RSA -keysize 2048 -validity 36500 -keystore myapp.keystore参数含义很简单alias是别名keyalg指定非对称加密算法keysize是密钥长度validity是有效期单位是天。36500天正好是100年够用。生成过程中会要求设置密码等一堆信息这些信息一定要保存好证书文件也要放在安全的地方丢了或者密码忘了你的App就无法更新版本了这种事情我见过不止一次有的公司只能被迫换包名重新上架用户全部流失。云打包时需要注意的配置项我罗列一下常用的App图标、启动图可以上传替换默认的不然应用市场审核会被拒权限列表要按需勾选不要一上来就全部勾上比如你不需要短信读取就不要申请短信权限现在的应用市场对权限敏感程度很高Android的包名applicationId要提前定好一旦上架就不能改这个包名就是App在Android生态中的唯一标识。3.2 iOS打包证书、描述文件和上架iOS的打包比Android麻烦得多因为苹果的生态是封闭的。首先你需要一个付费的Apple开发者账号一年99美元然后生成证书Certificate和描述文件Provisioning Profile。iOS的证书分开发证书和发布证书开发证书用于调试发布证书用于上架App Store。对应的描述文件也有Development和Distribution之分。生成流程是这样的在Mac的钥匙串访问里创建证书签名请求文件然后登录Apple开发者后台在Certificates模块上传这个请求文件生成证书接着在Identifiers里注册App ID这个App ID的Bundle Identifier必须和uni-app里的AppID一致不然装不上再在Profiles模块创建描述文件关联之前的证书和App ID最后把证书和描述文件下载下来导入到HBuilderX的云打包配置里选择对应的发布证书就可以打包了。这里有一个很多新手踩过的坑开发和发布使用两套不同的证书和描述文件而在HBuilderX里如果只配置了开发证书你可能可以在真机上调试但一旦要云打包发布就会一直提示找不到有效的发布证书。我建议从项目开始就同时生成开发和发布两套证书放在同一个钥匙串里管理别等到要上架了再手忙脚乱。iOS上架流程和安卓市场类似需要到App Store Connect里注册应用、填写描述、上传截图、配置隐私政策然后提交审核。苹果审核比较严格如果你的App涉及用户生成内容或者需要账号登录一般会要求提供演示账号如果用了位置权限还要在代码里说明用途而且App Store Connect的隐私信息里也要如实填写。3.3 离线打包和uts插件的集成方式离线打包我上面提过是团队正式项目中比较推荐的方案。但很多开发者第一次接触离线打包这个概念时会误以为离线打包就是不要联网就能打包其实不是。它的意思是打包过程在本地完成不依赖DCloud的云端服务你需要使用DCloud提供的离线SDK自己创建一个原生的Android或iOS工程把uni-app编译后的资源放进去再构建成安装包。离线打包的第一步是去DCloud官网下载与HBuilderX版本号严格对应的离线SDK。这个版本对应关系非常重要我用过不止一次因为SDK版本和HBuilderX版本不一致导致App打不开或者原生插件调不起来。确认版本后Android端用Android Studio打开SDK里的工程模板把uni-app项目编译出来的资源文件通常位于unpackage/dist/build/app-plus拷贝到工程指定目录然后配置包名、签名等等最后正常用Gradle构建。那uts插件怎么集成到离线打包里呢uts插件是DCloud主推的一种插件形态它让你用TypeScript语法来写原生逻辑然后会被编译成Android的aar或iOS的framework。在HBuilderX项目里你用右键创建一个带uts插件的uni_modules目录然后在插件目录下新建utssdk文件按规范写代码。云打包时HBuilderX会自动处理uts插件的编译和集成但离线打包就需要手动把编译产物集成到原生工程中。具体步骤如下在HBuilderX项目里右键uts插件目录选择本地打包插件或类似的操作把插件编译成Android Library或者iOS FrameworkAndroid端拿到aar文件之后放到原生工程的libs目录然后在build.gradle里加一行implementation files(libs/xxx.aar)iOS端则是把framework拖入Xcode工程并确保Embed属性设置为Embed Sign之后再在原生工程里初始化uni-app的SDK时注册这个插件模块并在App代码里引入。整个过程稍微繁琐但熟悉之后其实很有规律。需要注意的点uts插件在云打包和离线打包中的兼容性必须确认有些API只在云打包环境里有有些只能在原生工程里调用这些在官方文档的API支持矩阵里都能查到。而且升级HBuilderX版本后依赖的uts插件编译产物很可能需要重新生成不要直接拿旧产物集成到新SDK里。3.4 App端打包后websocket连不上是我见过最多的问题我看到热搜词里有一条websocket运行到h5可以连接打包为app连接不了这个我太熟了几乎每个做即时通信项目的人都会遇到。现象很明显在H5端页面里WebSocket连接正常消息收发都没问题但一旦打包成App装到手机上连接就失败报错信息五花八门有的直接显示WebSocket connection failed有的只是控制台里飘红。这个问题的根源绝大多数出在明文流量限制上。从Android 9API 28开始系统默认禁止应用使用明文HTTP流量WebSocket的ws://协议就属于明文流量所以被系统拦截了。解决方案有两个方向。第一个方向是改用wss://协议。如果你的服务器支持WebSocket over TLS直接在前端代码里把ws://改成wss://这个方案最干净既能解决Android明文限制也能避免iOS的App Transport SecurityATS限制。第二个方向是如果你只有ws://不想改后端就需要在应用里明文声明允许明文流量。在uni-app云打包时可以在manifest.json的App权限配置里找到Android相关的选项勾选使用支持HTTP或者配置usesCleartextTraffic属性为true。如果你走的是离线打包就得在AndroidManifest.xml的application节点下加一行application android:usesCleartextTraffictrue ...但这里有个实际工程里值得注意的地方直接把usesCleartextTraffic设为true意味着整个App的所有明文流量都放行了这在安全上是有风险的。更精细的做法是用networkSecurityConfig来配置只允许特定域名走明文其他域名仍然强制https。iOS端同样有类似问题ATS默认禁止非HTTPS连接ws会被拦。在uni-app云打包配置里可以设置iOS隐私权限中的ATS配置选择NSAllowsArbitraryLoads为true不过这会让苹果审核时的记录难看我一般建议能上wss就上wss别为省后端那点事给自己埋雷。另外还有一个容易忽略的点如果你在App里连接的是局域网地址比如192.168.x.x或者内网IP很多手机上还会出现连接超时。这倒不是协议问题而是App没有网络权限。排查方法是检查manifest.json里有没有勾选访问网络状态和网络权限另外如果目标手机是鸿蒙或者部分国产ROM后台运行时的网络权限也可能被系统限制需要在系统设置里把App的后台联网开关打开。4. 发布上线与版本更新从应用到市场到服务器分发4.1 微信小程序提审注意什么小程序开发完在微信开发者工具里点击上传工具会提示你填写版本号和项目备注。这个版本号想清楚再填它会显示在微信公众平台的版本管理列表里而且很多后台系统会拿它和线上的版本做比较最好和你的manifest里配置的版本号保持一致。上传成功之后登录微信公众平台在管理 - 版本管理里就能看到刚上传的版本点提交审核后会进入一个填写审核信息的页面包括功能页面、测试账号、隐私说明等。一审被驳回最多的情况是哪几种我总结下来主要就这几类一是隐私政策不完善尤其是涉及用户信息收集的必须在页面里明确公示并获取用户同意二是页面不能正常打开或出现空白页这种多半是代码在审核环境的权限处理有问题三是类目选择不对比如你是做电商的选了工具类就会被打回。所以提交之前自己先在小程序体验版里把核心路径完整跑一遍比任何检查都有效。提交审核通过之后按钮变成发布点击发布后新版本才会真正对用户生效。微信没有灰度发布机制只能全量发布如果担心出问题可以先用按比例发布的功能这个需要申请开通一般正常的非个人主体小程序都能开。4.2 App上架应用市场的材料清单Android应用市场比较多华为、小米、OPPO、vivo、应用宝这些都是大头。上架不仅要求你有签名APK还需要一堆材料。我在没有准备的情况下走了一次全流程结果被反复打回很浪费时间。硬性材料至少包括这些软件著作权证书软著或者版权证明这个很多市场是必须的要么自己申请要么找代理一般需要1-2个月所以一定要提前准备App的隐私政策网页并且要在App内能访问到最好放在首页或者设置页里应用截图、图标、应用描述每个市场的尺寸要求还不完全一样部分市场要求提供测试账号或演示视频。这些材料准备好之后按各个开放平台的指引逐步提交。审核周期一般不固定快的当天慢的3-7个工作日都有。如果急着上架建议优先提交应用宝和华为这两家量比较大审核效率相对也高。另外提醒一句不同市场的包名必须一致签名也必须同一套不然用户从不同市场下载的版本会被系统当成两个应用也无法互相覆盖升级。4.3 用Git和服务器实现APK分发和更新热搜词里有一条很具体android studio生成的apk如何通过git推送发布到服务器以便后续更新。这个问题我理解是开发团队内部想快速把APK分发给测试或者部分用户又不想每次都手动拷贝文件。其实方案很简单本质就是APK先提交到Git仓库服务器拉取最新APK然后在Web服务器上生成可下载的链接App内检测版本提示升级。具体做法可以分几步用Android Studio打一个release签名的APK在项目目录里建一个release目录然后Git提交并推送到远端注意如果APK太大或者每次都提交仓库会越来越臃肿所以可以考虑用Git LFS来管理APK文件。服务器端写一个简单的脚本定期拉取最新代码比如cron定时任务或者通过GitHub/Gitee的Webhook触发把最新APK复制到nginx的静态目录。同时在服务器上维护一个version.json内容大致是这个样子{ version: 1.2.0, versionCode: 12, url: https://download.example.com/app/1.2.0.apk, desc: 修复了xx问题优化了xx功能 }App端在启动时请求这个version.json和本地当前版本号对比如果服务器版本更新就弹窗提示用户确认后调用plus.runtime.openURL跳转浏览器下载APK并引导安装。这个方案我用下来觉得很适合中小团队内测分发成本低逻辑透明还可以配合二维码直接在群里发下载链接。坑也有主要是Android 8以上安装非应用市场APK时会默认拦截未知来源应用需要在代码里检查是否有安装未知应用的权限或者引导用户去系统设置里允许。另一个坑是下载链接必须走HTTPS否则不少浏览器会拦截下载。如果你没有现成的HTTPS域名微信里也会提示非安全链接体验会很差。5. 高频打包报错与排查技巧实录5.1 vue打包后布局异常怎么排查热搜词里有一条vue 打包后 布局异常这其实不单是uni-app的问题所有Vue项目都可能遇到。但我发现uni-app项目里最常见的原因是静态资源路径和CSS样式隔离问题。本地运行正常打包后样式全乱了先不要慌按顺序排查。第一步看资源路径uni-app里图片等静态资源要放在static目录而不是项目的assets或者src目录里否则打包后路径会找不到。第二步看样式污染如果用了多个组件库或者全局样式和局部样式混合打包后CSS加载顺序发生变化就可能出现样式覆盖错误解决方法是给样式加上scope或者把公共样式单独拆出来统一管理。第三步查编译缓存有时候HBuilderX编译缓存脏了也会导致样式异常这时候执行一次重新编译或者删除unpackage目录重新打包就好。最后一招是看浏览器控制台的报错很多时候打包后的JS报错会导致组件没有正常渲染表现就是布局混乱这种问题要优先解决JS错误。5.2 uni-app network: unavailable是什么意思uni-app network: unavailable这个报错经常出现在App端真机调试或者运行的时候一出现就说明App里根本检测不到网络。最开始遇到这个问题我第一反应是手机没网结果其他应用都正常后来才发现是权限问题。检查一下manifest.json里的App模块配置确认一下是否勾选了网络权限如果没勾重新打包就好了。不过要小心有的项目是后来才加上网络权限的但云打包时只重新打包了主包没有重新打自定义基座那就还是旧基座在跑权限配置自然不生效。这种情况下需要把自定义基座也重新打包安装一遍问题才能彻底解决。另外如果你的App是在Android模拟器里运行的模拟器自身的网络模式也可能导致这个报错试着重启模拟器或者切换网络模式。iOS模拟器一般没这个问题但如果是真机还要确定一下应用有没有被系统限制Wi-Fi和蜂窝网络的访问权限。5.3 webpack打包优化配置的几个实用手段uni-app默认使用webpack进行打包项目变大之后打包体积和速度都会成为痛点。优化方向可以从两个角度入手缩短打包时间、减小产物体积。缩短时间最直接的办法是配置模块解析范围在vue.config.js或者webpack配置里加上resolve的modules和extensions的优化减少查找路径。另外开启thread-loader给loader增加多线程处理也能明显提升速度。减小体积的话首选方案是路由页面的懒加载uni-app用pages.json配置页面时会自动做分包处理但H5端需要确认路由懒加载是否生效可以在构建产物里看每个页面是否被拆成了独立chunk。其次是用压缩插件来压缩JS和CSSuni-app在发行模式下默认会走压缩但如果你用的是自定义的webpack配置很可能把压缩步骤覆盖掉了要仔细核对。还有一个经常被推荐的优化把体积很大又不会频繁变动的基础库通过externals配置排除出打包结果然后改用CDN引入。比如vue全家桶、图表库等如果项目需要用CDN加载就需要注意版本一致性以及某些库不支持UMD的情况实际项目中我建议只对非常成熟稳定的库做这个操作否则CDN一挂整个页面就废了。5.4 常见报错速查表我把这些年遇到的高频打包报错整理成一个速查表方便大家定位报错现象常见原因解决办法打包后白屏JS报错、路由路径问题、资源路径错误浏览器控制台看JS错误检查publicPathApp连不上网络请求缺网络权限、明文流量限制manifest勾选网络权限配置usesCleartextTraffic微信小程序主包超限单包超过2MB配置分包subPackages云打包一直失败证书过期、SDK版本不匹配确认证书有效期确认HBuilderX版本和离线SDK一致自定义基座无法安装包名与签名不一致检查manifest里的AppID和证书签名是否匹配iOS证书找不到开发证书和发布证书混淆在钥匙串和开发者后台核对证书类型websocket连接失败ws明文流量限制改wss协议或配置网络安全策略离线打包后找不到uts插件插件编译产物没集成或SDK版本不匹配重新编译插件核对aar/framework是否嵌入遇到报错的时候先别急着复制粘贴到搜索引擎我的习惯是第一步看控制台最原始的报错信息第二步查manifest和构建配置第三步查版本对应关系。实际上百分之七八十的uni-app打包问题最后都归结为版本不一致、签名证书不统一、权限配置遗漏这三类跑不掉。再补一个我个人的经验不论你用的是云打包还是离线打包建议把打包过程中用到的所有版本信息记成一个文档包括HBuilderX版本号、离线SDK版本号、Android Gradle版本、uts插件编译时间、证书有效期等。有一次我排查一个非常诡异的App运行崩溃问题查来查去最后发现是临时升级了HBuilderX之后就打包了一个新包但离线SDK没同步升级原生代码和js代码的SDK版本对不上导致Api调用异常。版本记录真的能救命。关于打包发布我最后想掏心窝子说一句这套流程看似简单但每一步都关系到用户能不能顺利用上你的产品。证书和密钥保存好版本记录写清楚发布之前一定自己先跑一遍完整功能。能提前准备的材料不要等审核的时候才着急。哪怕只是一个内部测试的APK版本号、更新说明、下载链接这些细节认认真真对待你的团队协作效率会高很多。
返回列表