1. 打包之前先弄清楚你要把代码送去哪几个地方uni-app 项目打包这件事看着是点一下按钮实际能把人磨掉一层皮。我手上这个项目从立项到上线前后打了不下三十次包Android、iOS、小程序、H5 四条线全走了一遍中间踩的坑足够写满一个小本子。所谓 uniapp 项目打包说白了就是同一套 Vue 代码分别经过不同的编译器处理产出安装包、小程序代码包和静态网页资源这三种完全不同的交付物。它的价值很直接一份业务代码多端复用省掉重复开发的成本。但代价同样明显——每个平台的证书体系、体积限制、审核规则、运行时环境都不一样任何一个环节配置错位出来的包轻则装不上、重则白屏闪退排查起来还得从头翻日志。这篇记录适合谁看如果你刚接手一个 uni-app 项目正准备第一次出正式包或者你已经打过几次但总在签名、版本号、分包这几个地方卡住再或者你想搞清楚云打包和本地打包到底差在哪、证书到底怎么生成、包体积怎么压下来——下面的内容应该能帮你省掉大量的试错时间。我会按真实操作的顺序从环境准备一路写到问题排查中间该给的命令、该贴的配置、该算的参数都放进去你可以直接照着改。1.1 四条产物链路本质是四套编译器很多人把打包理解成一个动作实际上四条链路走的是四套完全独立的编译流程产物形态和运行环境差异极大。Android 端最终产出的是 APK 文件运行在设备的原生容器里WebView 渲染页面、原生插件提供能力需要签名证书和包名唯一标识。iOS 端产出 IPA除了签名证书还要描述文件并且必须绑定开发者账号不能像 Android 那样随便装。小程序端产出的是一堆 JS 和 WXML 文件压缩上传到平台后由平台自己的运行环境加载最大的硬约束是包体积——主包超过限制直接传不上去。H5 端产出的则是纯静态资源扔到任意静态服务器就能跑看似最简单但路由模式、资源基础路径、跨域配置这三样经常出问题。理解这条链路差异的意义在于你在某个端上遇到问题时能立刻判断出它属于编译期问题还是运行期问题。比如页面在浏览器里好好的打成 APK 后白屏那大概率不是业务代码写错了而是资源路径或者路由基础路径在原生容器里的解析方式不一样。再比如小程序端提示包体积超限那就跟代码逻辑无关纯粹是静态资源和依赖引入的问题。1.2 打包前必须确认的三件事正式出包之前,我会强制自己过一遍一个三件事的清单这三件事任何一件没确认打出来的包都是废的。第一是环境变量。项目里通常会有开发、测试、生产三套接口地址如果打包时忘了切换包能装上、能打开、登录也正常但所有请求都打到测试环境去了这种情况上线后才发现会非常被动。我的做法是把环境配置抽成一个独立文件通过打包命令的参数注入而不是靠手工修改注释。第二是版本号。版本号分为版本名称和版本编码两个字段前者给人看后者给系统比对更新用。每次出包这两个值必须同步递增只改其中一个会导致应用内更新检测失效或者应用市场拒绝上传。我见过有同事连续打了五个包版本编码都没变结果线上用户一个更新提示都没收到。第三是签名文件。Android 的签名证书一旦丢失后续所有版本都无法覆盖安装只能换包名重新上架用户数据全部丢失。这个文件我会在项目文件夹之外单独备份一份并且在团队文档里记录证书别名和密码的存放位置。这三件事确认完毕才轮到真正动手。2. 打包环境与工程配置的准备工作环境这块有两种常见路线一种是直接用官方 IDE 图形界面操作另一种是走命令行工具链。两条路线各有适用场景选错了会在中途反复折腾。2.1 图形化工具与命令行工具怎么选图形化 IDE 的优势是零门槛装好导入项目就能点运行、点打包证书配置和云端打包都在界面里填适合项目初期快速验证和单人开发。缺点是它把打包过程封装得太彻底出问题时你只能看到一句笼统的失败提示很难定位到底卡在编译、签名还是上传环节。命令行工具链的优势是所有配置都落在工程文件里可以纳入版本管理可以写进自动化流程也可以在流水线上跑。缺点是初始化配置麻烦需要手动装依赖、配置编译脚本、处理平台差异。我的实际做法是开发期用图形化工具出正式包走命令行。日常改代码、真机调试、快速预览全都用 IDE一旦要打给测试或上线的包切到命令行执行固定脚本保证每次产出的过程完全一致不会因为某次界面上的勾选项忘了改而出现差异。命令行初始化需要把项目转换成命令行工程结构核心是安装对应的编译依赖包和平台相关的构建工具。这里有个容易被忽略的点依赖包的版本必须和项目使用的框架版本对齐版本错配最典型的症状是编译能过但运行时报某个运行时模块找不到。我一般会在工程根目录的依赖清单里把相关包全部锁定成精确版本号不用范围符号避免某次安装依赖时自动升级导致打包结果变化。2.2 应用配置清单里那些容易被忽略的字段项目根目录下的应用配置文件是整个打包流程的中枢很多打包后的异常都源于这里的字段没填对。下面几个字段是我踩过坑之后每次必查的。应用唯一标识这个值在打包时必须保证唯一且一旦上架就不能再改。它的作用类似于身份证号服务端推送、应用市场更新、第三方登录回调全都依赖它。开发阶段随便填也能跑但正式出包前一定要确认它是最终值。版本名称与版本编码前者是展示给用户看的字符串可以带小数点后者是整数系统用它判断新旧。这里的规则是版本编码必须严格递增不能重复、不能回退。如果服务端做了强制更新逻辑那版本编码的递增策略还要和更新接口的判定逻辑对齐。屏幕方向与启动图配置如果是横屏应用必须在这里显式声明横屏否则打包后在设备上会被强制竖屏显示页面布局全部错乱。启动图的分辨率也要按目标设备的主流尺寸准备尺寸不对会出现拉伸或者黑边。原生模块清单用到定位、推送、支付、相机这些能力时需要在配置里声明对应的模块。漏声明的表现是代码里调用相关接口时直接返回失败但在开发工具里模拟运行又是正常的因为工具环境默认开了全套能力。这个问题我在第一次打正式包时遇到过排查了大半天才反应过来。权限声明Android 端尤其要注意每个权限都要有明确的业务用途。新版本系统对权限管控越来越严没声明的权限在运行时申请会被直接拒绝。通知类权限在较新的系统版本上还需要单独申请不能只靠清单声明。2.3 版本号该怎么管才不混乱版本号管理看起来是个小问题但它直接决定了更新链路能不能跑通。我采用的是三段式规则主版本号对应大的功能迭代次版本号对应功能新增修订号对应缺陷修复。版本编码则用一个简单的公式从版本名称推导出来保证两者永远同步。一个实用的做法是在打包脚本里加入校验执行打包前先读取当前的版本配置和应用市场后台记录的最新版本比对如果编码没有递增就直接中断打包并报错。这个校验拦住过我好几次因为手误忘记改版本号的情况。对于需要内测分发的场景可以在版本名称后面追加构建时间戳这样同一套代码打多个渠道包时不会互相覆盖。3. Android 端打包全流程实录Android 是四条链路里最容易上手、也最容易在签名环节翻车的一条。下面按实际操作顺序走一遍。3.1 签名证书的生成与保管签名证书是 Android 应用的身份凭证必须用工具生成并长期保存。生成命令的基本形式是这样keytool -genkeypair -alias myapp \ -keyalg RSA -keysize 2048 -validity 36500 \ -keystore myapp.keystore -storetype JKS参数逐项说明-alias是证书别名后续打包时要填同一个别名-keyalg RSA -keysize 2048指定密钥算法和长度2048 位是目前的安全基线-validity 36500是有效期天数换算下来约一百年这是行业惯例目的是避免证书过期导致应用无法更新-storetype JKS指定密钥库格式。执行后会依次提示输入密码、姓名、组织、城市等信息其中密码和别名是打包时必须准确填写的。生成之后要立刻做三件事把证书文件复制到工程目录之外的安全位置备份把密码和别名写进团队的密钥管理文档如果有多个渠道需要独立签名用同一个证书文件即可不必重复生成。这里有个血泪教训——我曾经把证书放在项目目录里某次清理缓存时误删又没有备份最后只能换包名重新上架老用户全部流失。3.2 云打包与本地打包的差异云打包是把工程上传到打包服务端在远端机器上完成编译和签名返回一个安装包。它的优点是省事不用在本地装原生开发环境适合没有 Android 原生基础的团队。缺点是受网络和排队影响打包可能需要等待而且出问题时看不到完整的构建日志。另外免费额度有次数限制频繁出包的阶段要规划一下。本地打包则需要下载离线打包资源包用原生开发工具打开工程配置签名和模块后编译。它的优点是全过程可控构建日志完整打包速度取决于本机性能适合需要频繁出包或者要深度定制原生能力的项目。缺点是要装一大套原生环境配置项也多第一次搞通常要折腾半天。我的选择是日常验证和小版本用云打包重要版本和需要验证原生插件兼容性的场景走本地打包。两者的配置要保持一致特别是签名信息和原生模块清单否则可能出现在云打包正常、本地打包报错的情况。3.3 关键参数与产物校验打包时几个参数直接影响包的兼容性和体积。最低支持系统版本决定了应用能装到多旧的设备上设得过高会流失一部分用户设得过低则要处理更多兼容逻辑。目标系统版本影响系统对新特性的适配行为应用市场通常对它有最低要求太旧会被拒。CPU 架构过滤决定打包哪些架构的原生库只打主流架构能显著减小体积但会牺牲对老设备的支持。产物校验我一般走三步先看安装包体积是否在预期区间突然变大通常意味着误引入了大文件或者依赖再在至少两个不同品牌的真机上安装运行重点验证首页加载、登录、核心业务链路最后检查应用信息里的版本名称和版本编码是否与配置一致。这三步走完包才算能用。4. iOS 端打包绕不开的证书体系iOS 这条链路是四条里门槛最高的核心难点不在编译而在证书和描述文件的匹配关系上。4.1 证书类型与描述文件的关系iOS 的签名体系由两部分组成证书证明你是谁描述文件说明这个应用能装到哪些设备上。开发证书配合开发描述文件只能装到事先登记过的测试设备上发布证书配合发布描述文件才能提交到应用市场。两者的绑定关系通过应用唯一标识串联标识必须完全一致一个字符都不能差。我遇到过一次特别隐蔽的问题描述文件里的标识用的是通配符形式看起来能匹配任何应用但应用内使用了推送能力而推送要求标识必须精确匹配结果打包能成功、安装也正常就是收不到推送。后来把标识改成精确值重新生成描述文件才解决。申请流程大致是在开发者后台创建应用标识并勾选需要的能力生成对应类型的证书并下载创建描述文件时选择证书和设备最后导出下载。证书下载后要导入本机钥匙串导出成带密码的交换格式文件打包时连同密码一起填进配置。描述文件则直接放进工程或者由打包工具自动匹配。4.2 打包流程与上架前的自检打包前要在配置里填好应用标识、版本号、构建号、显示名称、图标和启动图。构建号相当于 Android 的版本编码每次上传必须递增重复的构建号会被直接拒绝。上传环节有两种方式用专用上传工具或者用原生开发工具直接上传。上传成功不代表就能发布还需要在后台等待构建处理完成处理时间从几分钟到几十分钟不等。处理完成后才能添加测试人员或者提交审核。上架自检清单我固定这几项所有涉及隐私的能力是否都配置了用途说明文案缺文案会直接被拒是否声明了数据收集情况这个现在审核非常严格应用内是否有完整的账号注销入口截图和描述是否与实际功能一致。这几项每一条都曾经让我的版本被打回过。5. 小程序与 H5 端的打包要点这两个端不需要证书但各有各的硬约束。5.1 小程序主包体积的红线小程序对包体积的限制是最硬的技术约束主包不能超过 2MB单个分包不能超过 2MB所有分包加主包总计不能超过 20MB。这些数字以平台官方文档为准会随政策调整打包前最好确认一下当前限制。体积超标的原因通常集中在三处。一是静态图片没做处理直接在代码里引用了几张大图二是引入了完整的第三方组件库却只用了其中一两个组件三是所有页面都堆在主包里没有做分包。对应的解法也很明确。图片全部上传到对象存储代码里只保留网络地址本地最多留几个小图标。组件库改成按需引入只引实际用到的部分。页面按业务模块拆分到分包主包只保留首页、登录、个人中心这类高频入口。分包配置写在页面配置文件的对应节点里结构和主包页面列表基本一致指定根目录和页面路径即可。还可以配置分包预下载规则让用户在首页停留时提前把常用分包拉下来减少进入时的白屏时间。5.2 H5 打包的资源路径与路由模式H5 端打包后是一堆静态文件扔到服务器就能访问。但有两个配置项如果不管部署后必然出问题。资源基础路径如果应用部署在域名的子目录下比如https://example.com/app/必须把基础路径配置成/app/否则所有资源请求都会指向根目录导致 404。配置在应用配置文件的网页端节点里改完重新打包即可。路由模式默认的哈希模式会在地址栏带一个井号刷新不会 404兼容性最好。如果要用更美观的路径模式就必须在服务器上配置一条回退规则把所有找不到的路径都指向入口页面否则用户刷新页面会直接 404。这个配置在不同服务器上写法不一样部署前一定要和运维确认。另外打包时要确认是否开启了代码压缩和资源指纹生产环境建议开启前者减小体积后者解决浏览器缓存导致的更新不生效。调试用的源码映射在生产包里建议关闭既减小体积也避免源码泄露。6. 包体积优化与打包性能调优体积优化是个持续动作不是打包前临时抱佛脚能干完的。6.1 依赖与静态资源的瘦身最有效的三招是删掉没用的依赖、图片走网络、开启摇树优化。第一招需要定期检查依赖清单把引入后完全没用到的包移除尤其是一些工具库往往引入时图方便整包引入实际只用了其中一个函数。第二招前面提过本地图片能删就删。第三招是构建配置里的选项开启后未引用的代码不会被打进产物对体积影响很明显。还有一个容易被忽略的点是国际化资源。如果项目没有多语言需求却引入了完整的语言包体积会白涨一大截。同理一些 UI 组件库的图标字体全集也是体积大户能按需引入就别整包引。6.2 条件编译与分包策略的配合条件编译是 uni-app 里非常关键的能力写法是用注释包裹代码块并标注目标平台编译时只会保留对应平台的代码。举个例子调用设备原生能力时如果这段代码在小程序端执行会直接报错就必须用条件编译包起来。// #ifdef APP-PLUS const deviceInfo plus.device.getInfo(); // #endif // #ifdef H5 const isMobile /Mobile/.test(navigator.userAgent); // #endif实际项目里我习惯把平台差异逻辑集中到独立的适配层文件里业务代码只调用统一的接口名具体实现在适配层内用条件编译区分。这样做的好处是业务代码干净出问题时定位范围也小。分包策略则要和条件编译配合某个只在 App 端使用的重型功能模块可以整体放到一个分包里小程序端因为条件编译直接把入口屏蔽掉那个分包就不会被加载。7. 常见问题排查速查表下面这些是我实际遇到过并且记录下来的问题按现象归类方便对照排查。现象高概率原因排查方向安装包能装但打开白屏资源基础路径配置错误检查网页端基础路径与部署目录是否一致真机调试正常打包后接口 404环境变量未切换确认打包命令注入的是生产环境配置应用内更新检测不到新版本版本编码未递增比对配置值与市场后台记录值小程序上传提示主包超限图片未外链或未分包统计主包内静态资源与页面数量安装包解析失败无法安装签名不一致或架构不匹配检查证书别名密码与目标架构清单上架构建版本长时间不出现描述文件与应用标识不匹配核对标识精确值与能力勾选项某原生能力调用直接失败模块未在配置中声明对照能力清单补齐模块声明页面在浏览器正常原生端样式错乱使用了平台不支持的样式属性逐项排查布局方式与单位分包页面首次进入加载缓慢未配置预下载规则增加预下载并检查分包体积打包过程卡在编译阶段依赖版本不匹配锁定依赖版本并清理缓存重装排查时有个通用思路先判断问题出在编译期还是运行期。编译期问题的特征是打包直接失败或者产物缺失看构建日志基本能定位。运行期问题则是打包成功但行为异常这时候要对比开发环境和产物环境的差异最有效的手段是在产物里加日志把关键的环境变量、路径、接口地址打出来看。我遇到过最难受的一次是接口地址本身没问题但请求头里的某个字段在打包时被压缩工具误删了最后靠逐层打印请求配置才找出来。还有一类问题来自缓存。打包工具和依赖管理器都会缓存中间产物有时候代码改了但打包结果没变这时候清理缓存重装依赖往往能解决。我现在的习惯是每次出正式包前先执行一次完整清理虽然多花几分钟但能避免改了没生效这种最折磨人的情况。8. 几个我踩过之后形成习惯的细节证书和密码的备份我现在的做法是三重存放本机加密目录一份、离线存储介质一份、团队文档里记录存放位置不记录密码本身。这不是过度谨慎而是因为签名文件丢失的后果是不可逆的只能换应用标识重新开始。版本号我改成了一个显式的配置文件打包脚本第一步就是读取并校验校验规则是版本编码必须大于上一次打包时记录的值。这个小小的自动化拦截帮我避免了好几次低级失误。另外我养成了一个习惯每次出包后把产物、构建日志、当时的版本配置一起归档到一个带日期的目录里。看起来占空间但线上出问题时能快速比对这次和上次到底差了什么比凭记忆回溯可靠得多。有一次线上出现偶发的页面崩溃正是靠对比两个版本产物的构建日志发现是新引入的一个依赖在打包时被错误地打进了两个平台的分支定位时间从可能的半天缩短到二十分钟。最后再分享一个小技巧把打包必需的配置项整理成一份检查清单放在项目文档里每次出包前逐项打勾。清单不用长十来项就够关键是每完成一次出问题后的复盘就把新发现的问题点补进去。一年下来这份清单会变成团队里最有价值的东西之一比任何文档都实用。
阅读完成 · 觉得有帮助?