
我最早接触 uni-app 是在几年前当时手上一个项目要同时覆盖微信小程序、H5 网页和安卓 App 三个端。前后对比了好几个方案最终选它原因说白了就一句话把多端开发语言统一到 Vue 这一套语法上后端接口、公共组件、工具函数全部能复用。你写一个页面可以编译出小程序版本、浏览器版本、安卓安装包版本。这个收益对资源有限的小团队特别明显尤其是产品需要快速验证的时候。这篇文章是真正意义上的保姆级教程默认读者不是第一次写前端但没接触过 uni-app 的工程结构和打包链路。我会从开发工具安装开始把「项目创建」和「打包出 apk」这条容易卡住新人的链路完整走一遍。图形化创建、命令行脚手架、pages.json 和 manifest.json 配置、Android 云打包、本地打包再到我实际踩过的几个坑都会展开讲。跟着做半天内拿到一个能装到手机里的安装包问题不大。1. 项目概述与核心设计思路有人会觉得创建 uni-app 项目不就是点一下新建吗打包不就是按一下发行吗理论上确实如此但实际操作里「新建项目」之后会遇到运行器连不上、小程序工具调用失败、页面白屏「打包」之后会遇到签名不一致、图标缺失、权限没配置、安卓应用市场审核被驳回。这些问题往往比写业务代码更浪费时间。1.1 「项目创建」到底创建了什么新建 uni-app 项目本质上不是建一个文件夹而是搭好一套工程骨架。这套骨架里包含页面目录、静态资源目录、全局配置、应用入口、样式变量这些内容。它跟普通 Vue 项目很像但多了两个非常核心的配置文件pages.json 和 manifest.json。pages.json 负责整个应用的页面路由、导航栏、tabBar相当于整栋楼的「户型图」。manifest.json 负责应用在各平台的标识、权限、图标、SDK 配置相当于这栋楼的「产权证」。很多人创建完项目第一件事就是写页面完全不去管这两个文件等到打包上架的时候才发现路由错了、权限没开、图标全空白返工成本非常高。所以我的建议是新建项目后先花十分钟把这两个配置文件从头到尾看一遍明白每个字段控制的是什么。这十分钟省下来的可能是后面两小时的排查时间。1.2 创建与打包的核心设计主线如果给 uni-app 从零到上架画一条主线它是这样的创建工程 → 理解配置 → 开发页面 → 条件编译处理多端差异 → 配置图标权限 → 生成构建资源 → 用证书签名 → 打包出安装包 → 真机验证 → 上架渠道。这里值得一说的是「打包」为什么被单独拿出来当难点。打包动作本身是平台在做但对前端开发者来说难点集中在三个地方证书和签名规则、各平台的配置差异、打包前后依赖版本的一致性。这些知识不在 Vue 文档里也不在 CSS 教程里只能在项目实践里积累。我见过不少开发者在微信群问「为什么我打出来的包装到手机上提示签名不一致」「为什么昨天还能打的包今天突然失败」基本都是对打包背后的资源生成逻辑不了解。这篇文章的后半部分重点就是把这些逻辑讲明白。2. 环境准备与开发工具安装在创建项目之前先把开发环境整理好。这一节看起来琐碎但几乎所有新手卡住的点都出在工具没配对、路径没放对、联动没开启这三个问题上。2.1 工具清单与版本搭配先列一张表把需要用到的工具说清楚工具主要用途版本建议HBuilderX主开发工具负责创建项目、编写代码、云打包入口当前最新正式版微信开发者工具调试小程序端查看编译后的页面效果和报错信息最新稳定版Android Studio安卓本地打包、SDK 管理、模拟器运行最新稳定版Node.js命令行创建项目、执行 npm 脚本长期支持版 LTSJava 环境生成签名证书、本地打包构建时需要JDK 17 更稳妥这里必须强调一个原则不要一味追新。开发工具的配套体系对版本极度敏感HBuilderX 升级之后本地打包 SDK 的版本也要跟着匹配Node.js 版本过新或过旧都可能让工程脚本报错。我个人的习惯是团队里统一一套经过验证的版本组合出了环境问题大家能互相参照。2.2 安装时的三处关键配置第一处安装路径别有中文别有空格。工具类软件我一般统一放到 D 盘某个无中文目录下比如D:\dev_tools。这个习惯在本地打包阶段特别有用因为很多 Gradle 脚本和原生构建工具遇到中文路径会直接崩溃报错信息还特别难懂。第二处HBuilderX 和微信开发者工具的联动。HBuilderX 默认不能直接唤起微信开发者工具需要在微信开发者工具里打开设置 → 安全设置 → 开启服务端口。然后在 HBuilderX 的菜单栏选择运行 → 运行到小程序模拟器第一次会提示填写微信开发者工具的安装路径选择安装目录下的启动程序即可。如果端口没开运行时会提示「无法连接到开发者工具」这不是项目的问题是联动配置没做。第三处Android 真机调试时手机要开启开发者选项和 USB 调试。部分手机连上电脑后默认只充电需要在通知栏里把 USB 模式切换为文件传输或调试模式。数据线也要注意有些线只能充电不能传数据这类问题经常被当成代码 bug 排查半天。3. 创建uniapp项目图形界面与命令行两种方式环境准备好之后开始正式创建项目。这一节我把两种方式都讲一遍你可以根据自己的情况选。3.1 用 HBuilderX 可视化创建项目几分钟跑通第一个页面打开 HBuilderX点击文件 → 新建 → 项目在弹出的窗口左侧选择 uni-app 分类。项目名称建议用全英文比如my-app-demo不要带空格存放位置选一个自己记得住的目录。模板选择这里卡住过不少人。默认模板自带首页、消息、我的这几个 tabBar 示例页面还带 Vue 语法示例适合第一次跑通流程。空白模板只给最基础的页面结构适合已经清楚自己要做什么的人。新手阶段我建议直接用默认模板先把项目跑起来再慢慢清掉示例代码。如果你拿默认模板直接开始写正式业务后面对着一堆示例代码清理会有点痛苦。创建完成后点击 HBuilderX 顶部菜单的运行 → 运行到浏览器 → Chrome。第一次运行会自动编译编译完成后会弹出浏览器页面渲染正常就说明项目创建成功。浏览器里修改页面的文字保存后会自动刷新这个开发体验跟普通前端工程一样。运行到微信开发者工具的操作类似前提是前面说的联动配置已经完成编译完成后它会自动唤起微信开发者工具并打开页面。跑通这两个目标端之后「项目创建」这件事就不只是建了空壳你会亲眼看到同一套代码在浏览器和小程序里都能跑起来。这个正反馈对新手特别重要。3.2 用命令行脚手架创建 Vue3 Vite 工程可视化创建适合一个人快速起步但如果你所在团队已经有完整的 npm 工程体系希望把 uni-app 集成到统一的版本管理和持续集成流程里命令行脚手架更合适。以 Vue3 Vite 模板为例操作如下# 1. 用 degit 拉取官方 Vue3 模板到本地 npx degit dcloudio/uni-preset-vue#vite my-uniapp-demo # 2. 进入项目并安装依赖 cd my-uniapp-demo npm install # 3. 启动微信小程序编译模式 npm run dev:mp-weixin这里解释一下npx degit的作用是把远端模板仓库复制到本地不保留 git 历史相当于下载了一个纯净模板。执行完第三步后项目会在dist/dev/mp-weixin目录下生成编译产物用微信开发者工具直接导入这个目录就能看到页面。常用脚本还有npm run dev:h5对应浏览器调试npm run build:app对应生成 App 打包资源npm run build:h5对应构建 H5 产物。对刚接触 CLI 方式的读者我建议先跑通dev:mp-weixin因为微信开发者工具的报错信息相对直观方便你逐步理解编译过程。需要提醒的是命令行方式创建的工程如果想跑安卓真机调试最简单的方式是直接用 HBuilderX 的导入功能打开项目根目录然后通过 HBuilderX 的运行菜单连接手机。CLI 工程和 HBuilderX 工程共用同一套配置规范这一点不用担心。3.3 项目目录结构逐层拆解不管用哪种方式创建项目目录结构基本一致。以 HBuilderX 创建的默认工程为例├── pages/ │ ├── index/index.vue │ └── ... ├── static/ ├── uni_modules/ ├── components/ ├── App.vue ├── main.js ├── manifest.json ├── pages.json ├── uni.scss └── vite.config.jspages目录存放页面组件每个页面一个目录文件名和路由一一对应。static目录放静态资源比如本地图片、字体文件这些文件会原样打包进应用。uni_modules是插件市场下载的模块统一存放位置类似 npm 包的作用但它是 uni-app 生态特有的安装目录。components放自定义可复用组件如果组件只在某几个页面用也可以就近放在页面目录下。App.vue不是页面它是整个应用的生命周期入口onLaunch里可以做全局初始化比如拉取用户信息、检查更新。main.js是应用入口文件负责创建 Vue 实例。uni.scss存放全局样式变量可以定义主题色、通用间距方便所有页面引用。pages.json和manifest.json是全文最核心的两个配置文件下面单独说。3.4 pages.json 与 manifest.json 的第一次配置pages.json 其实是一个 JSON 路由表。第一次创建项目后我建议主动把里面的内容读一遍它长这样{ pages: [ { path: pages/index/index, style: { navigationBarTitleText: 首页 } } ], globalStyle: { navigationBarTextStyle: white, navigationBarTitleText: 我的应用, navigationBarBackgroundColor: #007AFF, backgroundColor: #F5F5F5 }, tabBar: { color: #7A7E83, selectedColor: #007AFF, list: [ { pagePath: pages/index/index, text: 首页 } ] } }pages数组的第一项是应用启动页这个顺序很重要改错了会导致启动时跳转到别的页面。globalStyle定义导航栏的全局样式各页面可以覆盖。tabBar是最多五个的底部导航配置注意文本颜色和选中颜色要符合设计稿这里很容易被忽略。常见错误是页面路径写错一个字母编译不报错运行起来白屏排查的时候要从控制台看路由报错。manifest.json 在 HBuilderX 里是以可视化界面展示的基本信息那一栏有应用名称、AppID。测试阶段用系统自带的测试 AppID 就够但正式发布前一定要到 uni-app 官方开发者平台申请正式 AppID否则打包上架会受阻。这里有个注意点最好不要手动去改 manifest.json 的源码尤其是一些标识字段HBuilderX 的可视化配置会在编译时自动注入手改容易改坏。图标、权限、隐私协议的配置都会在下一节展开讲。4. 打包前的工程配置条件编译与资源准备很多人把代码写完就急着打包结果在打包环节反复折腾。其实打包之前有几项配置如果不处理后面几乎是必定出问题。这一节讲条件编译和资源准备都直接影响最终安装包的行为。4.1 条件编译一套代码处理不同平台的差异条件编译是 uni-app 最核心的能力之一它的意思是在编译阶段根据当前目标平台只保留对应的代码块。它的语法长得像注释但编译器会识别并处理。举一个最常见的 JS 条件编译例子// #ifdef APP-PLUS console.log(这段代码只在 App 端运行); // #endif // #ifndef MP-WEIXIN console.log(这段代码在除了微信小程序之外的其他端运行); // #endif前缀含义要记牢#ifdef表示「如果定义了该平台则编译」#ifndef表示「如果没定义该平台则编译」。常见平台标识有APP-PLUS、MP-WEIXIN、H5分别对应 App 端、微信小程序端、网页端。实际项目里条件编译最常见的用途是处理导航栏差异。比如小程序端用系统原生导航栏App 端想用自定义导航栏加渐变效果就可以在页面模板里放两个导航容器用条件编译标记区分编译成小程序时只保留小程序那份代码编译成 App 时只保留 App 那份代码。业务逻辑里也可以做不同平台的统计上报埋点。这里要特别强调一点条件编译是编译期行为被排除的代码不会进入最终包体所以它比运行时判断彻底得多。代价是语法要求苛刻注释里的#ifdef少一个字符整个块就可能被当成普通代码打进包里还会产生莫名其妙的报错。我见过一个同事把#ifdef写成了#ifdefs字节码处理的时候编译通过但代码执行不到排查了很久。CSS 同样支持条件编译/* #ifdef APP-PLUS */ .custom-class { height: calc(100vh - var(--status-bar-height)); } /* #endif */这种方式在做沉浸式状态栏、安全区域适配时非常常用。4.2 图标、启动图、应用名、权限与隐私协议配置打包前的资源准备重点看四个东西应用名称、图标、启动图、权限声明。应用名称在 manifest.json 可视化界面的基本信息里配置。不同平台的入口名称来自这个字段微信小程序显示的名称在微信公众平台设置App 桌面图标下的名称来自这里。打包前先确认应用名称不是默认的「uni-app」否则应用装到手机上显得特别不专业。图标建议准备一张 1024×1024 的 PNG 图片。HBuilderX 的图标配置界面支持一键生成各个尺寸和平台的图标它会自动裁剪出安卓各分辨率的图标。启动图和图标类似工具的自动化生成能力可以减少大量手工切图工作。这里有个细节如果图片包含透明通道某些安卓机型上桌面图标会出现黑底所以底图最好用不透明的纯色背景。权限声明是打包后能否正常使用的关键。manifest.json 里的权限配置分两类一类是基础权限比如网络、读写存储另一类是专项权限比如相机、定位、录音。如果你用了 uni-app 的拍照组件但没配置相机权限打包后调起相机会直接失败。定位权限更典型配置漏了真机上拿不到位置信息控制台还不一定报错。隐私协议是最近几年应用市场审核特别关注的点。manifest.json 里需要配置隐私弹窗的标题、内容和政策链接。用户在手机上第一次打开应用时会看到隐私协议弹窗点击同意才能继续用。如果应用没做这个弹窗安卓市场提交审核时大概率被驳回。我的建议是文案提前让法务或业务方输出不要临时拼凑。5. 安卓APK打包完整实操云打包路径配置做完进入真正的打包环节。这一节先讲云打包因为它对前端开发者最友好不需要安装整套 Android SDK。5.1 云打包的核心逻辑与适用场景云打包简单说就是把你的代码上传到云端构建服务器服务器完成 Android 依赖集成、资源合并、签名等操作最后返回一个安装包给你。它的优势是门槛低前端开发者不用搭建原生构建环境劣势是受网络和排队影响遇到高峰期可能要等。而且如果你要深度集成原生插件或者对包体有严格定制需求云打包不一定够用。云打包的典型使用场景有三个第一个是给测试同事出一版测试包第二个是打包完丢给设计师看还原度第三个是正式发布到安卓应用市场。前两个场景用公共测试证书就行第三个场景必须用正式签名。5.2 云打包完整操作步骤用 HBuilderX 走云打包的流程并不复杂第一步确认 manifest.json 里的应用名称、应用图标、权限、包名都配置好。第二步点击菜单栏发行 → 原生App-云打包。第三步在弹窗中选择平台为 Android。第四步选择证书。如果你有正式证书上传 keystore 文件并填别名和密码如果只是测试选择公共测试证书。第五步选择打包模式测试阶段用 debug 模式正式发行用 release 模式。第六步点击打包等待进度条走完下载生成的 apk 文件。拿到 apk 后先用数据线传到安卓手机上安装或者用模拟器安装测试。建议至少在一台 Android 13 以上机器和一台低版本机器上都装一下看看权限弹窗、页面适配有没有问题。如果手机上之前装过用公共测试证书签名的包再装正式证书签名的包会失败先卸载旧的再装就行。这个现象本质上是签名不同下面接着讲。5.3 Android 签名证书从生成到配置Android 的安装包必须经过数字签名系统靠签名识别应用作者身份。签名可以理解成你的「数字私章」同一把私章盖出来的包才能被认为是同一个应用才能实现覆盖升级。换签名等于换了个应用老用户无法直接覆盖安装更新这个教训很多团队都吃过。生成正式签名证书可以用 JDK 自带的 keytool 命令keytool -genkey -alias myalias -keyalg RSA -keysize 2048 -validity 36500 -keystore release.keystore这里每个参数都有实际意义。-alias是证书别名后面配置签名时会用到-keyalg RSA指定加密算法-keysize 2048是密钥长度太短有安全风险-validity 36500是有效期单位是天36500 天差不多一百年够正常业务使用了。执行命令后命令行会依次询问姓名、组织、城市等信息按真实情况填写即可。证书生成后有两件事必须做第一牢记密码忘了密码这串 keystore 就废了第二把 keystore 文件和密码一起存到公司的密码管理工具里如果是个人项目也要放到可靠的私有存储空间。提示凡是发布过应用市场的包后续版本必须用同一个证书签名。发行新版本时如果提示签名不一致说明你的证书或签名配置出了问题这比代码 bug 更麻烦。6. 安卓本地打包与 iOS 打包补充云打包解决了大部分常规需求但总有场景需要走本地打包。同时iOS 打包的证书体系跟 Android 完全不同这里一并做个补充说明。6.1 本地打包用 Android Studio 出正式 APK什么时候需要本地打包我觉得至少有这三种情况云打包排队太久项目需要集成自定义原生插件或者你对安装包体积、构建过程有强控制需求。本地打包的核心思路是用 HBuilderX 生成一份 App 资源包再把它塞进安卓原生工程里用 Android Studio 完成编译和签名。操作步骤如下在 HBuilderX 点击发行 → 原生App-本地打包 → 生成本地打包App资源。这一步会在unpackage/resources目录下生成__UNI__xxx命名的资源文件夹这个名称就是应用标识后面要用。去 uni-app 官方文档找到 Android 离线打包 SDK下载和你 HBuilderX 版本对应的 SDK 工程模板。版本对应关系极其重要新版 HBuilderX 升级后旧版离线 SDK 经常会编译失败。用 Android Studio 打开 SDK 工程模板把 HBuilderX 生成的资源文件夹复制到app/src/main/assets/apps目录下。修改工程里的包名配置把默认包名改成你自己申请的包名。在build.gradle里配置签名文件填写 keystore 路径、别名、密码。菜单栏选择 Build → Generate Signed APK按向导生成正式签名包。本地打包最大的坑在于版本不匹配。你打开 HBuilderX 用的 3.x 版本和离线 SDK 的版本必须对得上否则会遇到各种底层异常。遇到这类问题先去对照官方文档的版本说明别急着怀疑自己的代码。6.2 iOS 打包证书、描述文件与云打包iOS 生态的签名体系比安卓严格很多。iOS 打包必须有开发者账号整个流程可以归纳为三步。第一步生成 Certificate Signing Request 文件也就是证书请求文件。第二步在开发者后台创建 App ID也叫 Bundle Identifier相当于 iOS 的包名。第三步用证书请求文件在后台生成安装发布证书再生成对应的描述文件也就是.mobileprovision文件。描述文件会把证书、App ID、测试设备绑定在一起。在 HBuilderX 里做 iOS 云打包时需要上传两个关键文件导出后的.p12证书文件和.mobileprovision描述文件同时填写 Bundle ID。打包完成后会下载到一个.ipa文件测试阶段可以通过第三方分发平台装到手机上。这里要提醒一个很实际的问题iPhone 真机调试时描述文件里必须先添加测试设备的 UDID生成描述文件时要勾选对应设备否则安装到手机时报「无法安装此App」。第一次接触 iOS 打包的人经常在这里卡住。6.3 正式发布前的自检清单把自检清单列在这里每次打包前过一遍能省下大量返工时间检查项怎么检查包名正确Android 包名、iOS Bundle ID和市场后台保持一致签名一致新包签名与上市场版本一致不换证书图标无透明通道用不透明底图避免安卓桌面图标黑底版本号合理版本号和版本名称都升级不要低于线上版本权限声明完整相机、定位等权限已勾选且隐私协议文案已配置隐私弹窗可用首次安装打开弹窗出现并能跳转政策页面多版本真机测试至少覆盖新旧主流安卓版本和 iOS 版本7. 高频问题与排查技巧实录最后把常见问题按「现象 → 原因 → 解决思路」整理成表格。这里的内容不是我凭空想出来的都是日常答疑里反复出现的经典问题。7.1 从创建到安装的高频报错速查现象可能原因解决思路运行到微信开发者工具提示无法连接微信开发者工具服务端口未开启设置 → 安全设置 → 开启服务端口重试运行到手机白屏页面路由路径写错或组件报错打开手机端控制台查看路由和 JS 报错信息手机安装 apk 提示签名不一致签名证书和之前安装的包不一致卸载旧包重新安装发布时必须保证签名一致云打包提示图标缺失manifest 图标未生成或路径不对回到 HBuilderX 图标配置界面重新自动生成新版包体积突然变大静态资源被打包或基础库升级检查 static 目录是否有大图图片尽量走 CDNH5 端正常但 App 端报错代码包含浏览器专属 API检查代码里的document、window用法改为条件编译或 uni API排查这类问题有一个基础原则先看控制台报错再改代码。很多新人遇到问题第一反应是整个文件删了重写其实错误信息往往已经把原因说清楚了。H5 端和小程序端的控制台都能定位到具体文件和行号。7.2 本地打包编译出错与版本不匹配处理本地打包报错典型的是 Gradle 依赖拉不下来或者编译时出现某个类找不到。这类问题绝大多数是版本对应关系错了。我在实际项目里遇到过uniapp离线包和 HBuilderX 版本差了一个小版本结果运行时就崩溃日志里全是底层库加载失败的异常。处理方法是把 HBuilderX 升级到和离线 SDK 完全一致的版本重新生成资源重新构建。如果只是外部依赖下载失败可以通过配置国内镜像源解决Android Studio 里 Gradle 仓库地址也可以手动指定。还有一类报错和 JDK 版本有关比如提示Unsupported class file major version大概率是 JDK 版本太高或者太低调整到 17 一般能解决。另外提醒一句本地打包的报错日志在 Android Studio 底部 Build 窗口里完整复制日志再排查。不少人提问只发一句「打包失败」没有日志谁也没法判断具体原因。学会看日志是打包环节最重要的基本功。8. 实操心得与建议做了这么久 uni-app 项目我个人的实操体会是项目创建和打包这两件事看起来是体力活实际上考验的是对配置和签名的理解。第一个心得把 manifest.json 和 pages.json 的变更当成代码 Review 的一部分。配置文件的误改比业务代码的 bug 更难发现因为它在编译期不报错在运行期才暴露。团队里每次有人改这两个文件我都会要求把改动原因写清楚尤其是权限和 AppID 相关字段。第二个心得给每个项目建一个部署信息文档把包名、keystore 路径、证书密码、各市场账号、当前线上版本号全部记在里面。团队最怕的是核心同事离职后证书密码跟着消失。这个文档应该跟代码仓库放在一起并且有备份。最后分享一个小技巧。新同学进团队我从不让 TA 直接做业务而是让 TA 先把一个空项目从创建走到云打包再走一遍本地打包最后跑通 iOS 云打包。这个流程走完对新人对整个工程链路认知的建立比看十篇文档都有效。打包这件事不难难的是系统地理解它。