我用 uni-app 从零到一落地过一款鸿蒙 Next 应用,从 HBuilderX 初始化工程、写好页面,到跑上真机,再打包上架到应用市场,整个过程比想象中要顺,但中间也确实有几个配置和适配的坎。这篇文章我把整条链路拆开讲,重点放在 HBuilderX 怎么开发 uni-app 程序、入口文件与工程结构怎么理解、鸿蒙 Next 的适配差异,以及上架审核时容易忽略的材料和权限问题。适合刚入门的 uni-app 开发者,也适合手里已有小程序或 H5 项目、想低成本迁移进鸿蒙生态的团队参考。
1. 先说结论:uni-app 为什么能碰鸿蒙 Next
1.1 鸿蒙 Next 的真实处境
鸿蒙 Next 系统出来后,很多人的第一反应是:以前打一个安卓 APK 包,华为手机装上就能跑,现在这条路断了。新系统不再直接兼容安卓应用,这对个人开发者和小团队来说确实是个门槛——如果要用原生 ArkTS 重写整套业务,学习成本、时间成本都是肉眼可见的高。
但反过来看,这也是一次机会。鸿蒙 Next 的应用市场正处于生态建设的快速通道期,很多品类还没有被头部 App 占满。对个人开发者来说,早点把产品搬上去,能吃到一波流量红利。关键是得有一条低成本的跨端开发路径,而 uni-app 的编译能力刚好能覆盖这个需求。
1.2 为什么选 uni-app 而不是直接上 ArkTS
我身边不少朋友纠结过这个问题:直接用 ArkTS 写原生不好吗?当然好,官方支持最完整、性能上限最高。但现实是,如果你已经有了一套基于 Vue 的 uni-app 项目,或者主力技术栈是 Vue,那么用 ArkTS 重写的成本就不是十天半个月的事。
uni-app 的优势在于它把 Vue 语法翻译成多种端的目标代码,鸿蒙 Next 只是其中一个编译目标。同一套代码,小程序端、App 端、H5 端、鸿蒙端都能出包。选型时我也对比过 Taro、Flutter、React Native:Taro 对鸿蒙的适配链路不如 uni-app 直接,Flutter 在鸿蒙上的第三方生态还在完善,React Native 的鸿蒙支持更是要自己拼接不少胶水代码。综合下来,uni-app 是“用最少的迁移成本换一个可上架鸿蒙应用”的最优解。
注意:这里说的“低门槛”不是让团队放弃原生,而是指个人开发者、小团队、存量 uni-app 项目迁移时,uni-app 是性价比最高的方案。如果你的核心业务非常依赖原生地图、复杂动画、底层音视频处理,那该上 ArkTS 还是要上。
1.3 动手前先想清楚三件事
第一件事:你的应用是否有必须依赖原生组件的能力。比如人脸识别、实时音视频、自定义高性能绘制,这些在 uni-app 里虽然能通过插件市场找到封装方案,但可定制性有限。跑通“工具型小应用”没问题,复杂业务要先做技术验证。
第二件事:后端接口是否已经是 HTTPS,且域名有备案。鸿蒙 Next 的应用和很多移动端一样,对明文 HTTP 的限制比较严格,开发阶段还好,上架前如果还是 HTTP 接口,审核基本过不去。
第三件事:你的签名账号和设备准备好了没有。上架应用市场需要华为开发者账号,真机调试需要开启开发者模式的鸿蒙 Next 设备,或者用 DevEco Studio 自带的模拟器。这些最好在开发之前就备齐,别等页面写完了才去注册账号、等审核。
2. HBuilderX 环境与工程初始化
2.1 工具链版本匹配:HBuilderX、DevEco、SDK
我用的是 HBuilderX 4.62 版本,这个版本号在鸿蒙编译支持上已经算比较稳的了。撰写本文时,HBuilderX 对鸿蒙 Next 的支持已经包含在正式版里,不需要额外装插件。但有一环绕不开:最终打包还是得用 DevEco Studio。
如果你之前只做过小程序,没碰过鸿蒙原生,这个搭配会有点绕,简单说就是:HBuilderX 负责写代码和编译,生成一个完整的鸿蒙工程;DevEco Studio 负责打开这个工程,配置签名、跑模拟器、打正式包。所以两个工具都要装,DevEco Studio 里对应的鸿蒙 SDK 也要装好。
安装顺序建议是先装 HBuilderX,再装 DevEco Studio。DevEco Studio 安装时会让你选择 SDK 版本,这里别选太新的预览版,优先选正式发布的 API 版本,兼容性更好。我第一次用 Preview SDK 编译,运行阶段被一个底层接口变更折腾了大半天,后来退回正式版就没事了。
2.2 新建 uni-app 项目与目录结构
在 HBuilderX 里新建项目时,选“uni-app”模板,框架选 Vue 3,模版选默认空模版即可。很多人第一步就踩坑:选了“Hello uni-app”这种带大量示例插件的模板,里面冗余代码太多,编译到鸿蒙时体积大,还有不少与业务无关的插件请求。
新建完成后,目录结构是这样的,我建议你把每个文件的作用先搞清楚再写代码:
| 文件/目录 | 作用 |
|---|
| pages | 页面文件,每个页面一般由 .vue 文件构成 |
|---|
| static | 静态资源,图标、图片、本地 json 等 |
|---|
| main.js | 入口文件,创建应用实例、挂载 Pinia 等 |
|---|
| App.vue | 应用根组件,可以监听 onLaunch、onShow 等生命周期 |
|---|
| pages.json | 页面路由与导航栏、tabBar 配置 |
|---|
| manifest.json | 应用配置,包括应用ID、图标、模块权限、鸿蒙相关设置 |
|---|
| uni.scss | 全局样式变量 |
|---|
对小白来说,最常见的困惑是“页面到底从哪个文件开始”。实际启动顺序是:main.js 先执行,创建应用实例;然后是 App.vue 的 onLaunch 生命周期;最后根据 pages.json 里 pages 数组的第一项加载首页。这个过程不是单纯看代码就能理解的,建议你在 App.vue 的 onLaunch 里打一个 console.log,在首页 onLoad 里也打一个,运行时看控制台顺序,一次就清楚了。
2.3 入口文件与启动流程:main.js、App.vue、pages.json
很多从微信小程序转过来的开发者容易对入口文件犯迷糊。小程序里入口是 app.js、app.json、app.wxss 三件套,uni-app 里对应关系是:main.js 负责 JS 启动,App.vue 负责应用生命周期和全局样式,pages.json 负责路由和窗口表现。
main.js 通常是这样写的:
import App from './App.vue'
import { createSSRApp } from 'vue'
export function createApp() {
const app = createSSRApp(App)
return {
app
}
}
在 App.vue 里,你可以这样编写生命周期:
<script>
export default {
onLaunch() {
console.log('应用启动')
},
onShow() {
console.log('应用从前台切换到前台')
}
}
</script>
<style>
/* 全局样式 */
</style>
pages.json 里则要声明每个页面的路径、窗口标题和导航栏样式。编译到鸿蒙 Next 时,pages.json 中配置的导航栏会映射为鸿蒙侧的页面路由配置。需要注意的一点是:鸿蒙端对“首页必须存在且第一个呈现”有明确要求,pages.json 数组中的第一项就会被当作启动首页,别把启动页配置搞错。
2.4 manifest.json 配置要点
manifest.json 是很多人最容易忽略但坑最深的文件。在 HBuilderX 里它以可视化表单呈现,但本质是 JSON。对鸿蒙 Next 来说,重点关注以下几块:
基础配置里要填写应用名称和应用版本号,版本号后续上架会严格校验,建议一开始就按语义化版本维护。
图标和启动图,要准备多尺寸的 PNG,鸿蒙对圆角图标要求比较细,直接用 1024x1024 的主图生成最省事。
App 模块权限:按需勾选,比如定位、相机、网络。这里的原则是“用不到的权限绝对不勾”,鸿蒙上架审核对隐私合规很敏感,权限越多,审核问题越多。
鸿蒙相关配置:包括应用 ID、证书文件、描述文件。开发阶段可以先不填,但真机调试和打正式包时,这些必须要与 DevEco Studio 里配置的保持一致,否则构建出来的应用无法安装。
实操经验:我把 manifest.json 里所有能通过代码判断的权限都做成了运行时的动态申请,而不是在配置里一次性全声明。这样审核时被问到权限用途的概率会低很多。
3. 页面开发与核心功能实现
3.1 搭建首页、列表页、详情页
不管是工具类应用还是内容类应用,首页、列表页、详情页这三个结构基本能覆盖大多数场景。我的做法是先搭路由,再写页面骨架。
pages.json 路由配置示例:
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页"
}
},
{
"path": "pages/list/list",
"style": {
"navigationBarTitleText": "列表"
}
},
{
"path": "pages/detail/detail",
"style": {
"navigationBarTitleText": "详情"
}
}
]
}
首页的 .vue 文件大概是这个骨架:
<template>
<view class="page">
<view class="banner">今日推荐</view>
<view class="card-list">
<view v-for="item in list" :key="item.id" class="card" @click="goDetail(item.id)">
<text class="title">{{ item.title }}</text>
<text class="desc">{{ item.desc }}</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onLoad } from '@dcloudio/uni-app'
const list = ref([])
onLoad(() => {
// 拉取数据
})
function goDetail(id) {
uni.navigateTo({
url: '/pages/detail/detail?id=' + id
})
}
</script>
这里特别提醒一下:如果你用的是 Vue 3 script setup 写法,生命周期方法建议从
@dcloudio/uni-app
里 import,要和 Vue 自带的 onMounted 区分开。onLoad 是页面级生命周期,页面跳转传参只能靠它拿到;onMounted 是组件挂载,两者在 uni-app 里不能划等号。在鸿蒙 Next 端,路由栈和应用生命周期管理与小程序端有细微差别,页面 onLoad 的触发时机基本一致,但 onShow 会更容易被触发,因为鸿蒙的窗口切换生命周期更频繁。
3.2 数据请求与状态管理
数据请求是 uni-app 开发中的高频场景。官方提供的 uni.request 在小程序端和 H5 端都可用,鸿蒙 Next 端同样支持,但要注意以下几点。
第一,接口域名必须是 HTTPS,且证书有效。我在开发阶段用 HTTP 的测试接口,页面能正常出数据,但编译成鸿蒙包后请求直接失败,控制台报错说是网络安全配置问题。所以至少到真机调试阶段,就要把接口切到 HTTPS。
第二,请求封装要统一处理 loading、错误码、登录态。我的封装思路很简单:每个请求维护一个拦截器,进入时开启 loading,返回后统一判断 code,非 0 就弹出 toast。鸿蒙端 toast 组件和微信小程序的体验接近,但注意不要在短时间内连续调用 uni.showToast,鸿蒙会丢失前面的提示。
第三,状态管理。Vue 3 项目我推荐 Pinia,uni-app 官方也支持。页面多了以后,跨页面共享用户信息、设置项,全靠 props 和事件会非常痛苦。用 Pinia 的 store 管理全局状态,配合
pinia
插件,在 main.js 里挂载,然后在业务页面里
useStore()
即可。
3.3 原生能力调用与权限声明
一个鸿蒙 Next 应用如果要调用相机、定位、相册,必须提前声明权限。在 uni-app 里,这些能力通常用官方 API 封装,比如
uni.chooseImage
、
uni.getLocation
、
uni.scanCode
实际开发中我踩过的一个坑是:manifest.json 的模块权限没有勾选“相机”,结果在真机上调用扫码功能,一点反应都没有,控制台报 “Permission denied”。翻了一圈,不是代码问题,是权限声明问题。所以页面里用到什么能力,先在 manifest 可视化配置里勾选对应模块。
权限声明只是第一层,运行时系统还会弹窗询问用户是否授权。这里要处理好拒绝授权的场景。我在 App.vue 里做了一个“未授权引导”:当用户拒绝后,下一次操作时弹窗说明为什么要这个权限,引导去系统设置打开。鸿蒙 Next 的设置跳转接口在 uni-app 里可以通过
uni.openAppAuthorizeSetting
调用,亲测有效。
3.4 样式与 rpx 适配:鸿蒙下的坑
uni-app 开发 App 时可以选 rpx 作为样式单位,在鸿蒙 Next 端同样支持。rpx 的原理是把屏幕宽度分成 750 份,理论上跨设备等比缩放。但鸿蒙的部分设备屏幕尺寸跨度大,折叠屏、平板跑同一个应用时,卡片布局可能会出现拉伸变形的问题。
我的处理策略是:
基础间距用 rpx,保证小屏下比例正常。
大区块布局使用 flex 加百分比,避免内容被过度拉伸。
文字大小在关键标题上额外用
upx
动态计算,确保折叠屏展开时不至于缩得太小。
另外注意安全区。鸿蒙 Next 全面屏设备底部有一条手势条,如果页面有底部操作栏,要额外设置
padding-bottom: env(safe-area-inset-bottom)
。我凭 iPhone 时代的经验直接搬过来,发现鸿蒙同样支持这个 CSS 环境变量,但不同机型返回值有差异,需要在真机上再确认一遍。
4. 真机调试与鸿蒙适配
4.1 把编译产物跑起来:模拟器与真机
整个开发链路里,最容易让小白崩溃的就是“代码写了但不知道怎么跑起来”。在 HBuilderX 中,点击“运行到鸿蒙”的相关入口,选择设备或模拟器前,要确保 DevEco Studio 的 SDK 已经配置到 HBuilderX 里。
我第一次运行时,HBuilderX 提示找不到鸿蒙 SDK,实际上是因为 DevEco Studio 装好后,SDK 路径默认在用户目录下,而 HBuilderX 需要指定这个路径。填好路径后,编译过程会自动生成鸿蒙工程,然后拉起模拟器安装运行。
如果要在真机上调试,步骤会多两步:手机开启开发者模式并连接电脑,然后在 DevEco Studio 的签名配置页面生成调试证书。没有做调试签名的话,真机安装会被拒。
真机调试时我习惯的做法是:先用 HBuilderX 的“运行”查看页面代码层面的报错,再用 DevEco Studio 查看系统级日志。两边的日志一边看业务代码,一边看系统调用,配合起来排查问题速度快很多。
4.2 鸿蒙 Next 特有的适配差异
不做鸿蒙适配的话,很多页面在安卓/iOS 上正常,到了鸿蒙就出现各种小问题。我整理几个高概率碰到的差异点:
| 差异点 | 现象 |
|---|
我的处理方案
|
| 键盘遮挡输入框 | 底部输入框被弹出键盘盖住 |
|---|
使用
adjust-position
或监听键盘高度做位移
|
| 状态栏高度 | 页面顶部内容被状态栏吞掉 |
|---|
使用
uni.getSystemInfo
获取状态栏高度,动态设置 padding
|
| 导航栏返回 | 手势返回时页面状态没有触发 onShow |
|---|
在 onShow 里做数据刷新,而不是依赖返回事件
|
| 字体渲染 | 部分字体在鸿蒙下偏细、偏小 |
|---|
全局指定系统默认字体栈,不引入过细的自定义字体
|
| 下拉刷新 | 原生下拉刷新样式不一致 |
|---|
关闭导航栏原生下拉,用自定义刷新组件统一视觉
|
尤其是键盘遮挡问题,在鸿蒙 Next 的几个版本上表现不一致,不能只做一套方案。我的最终做法是:单行输入框用系统默认的弹出策略,多行输入框手动监听键盘高度,给页面底部留出安全空间。这个细节在审核演示视频里会很加分,因为操作流畅性一眼就能看出来。
4.3 首屏性能和长列表优化
鸿蒙 Next 端毕竟是原生渲染,uni-app 编译出来的页面在性能上不如纯 ArkTS 是正常的,但通过优化能让体验接近原生。我重点做了三件事:
第一,首页的请求并发控制。不要在页面的 onLoad 里同时发五个请求,尽量合并接口,或者用 Promise.all 串行控制,减少首屏加载时长。鸿蒙端的网络并发限制在某些版本上比较保守,一次并发太多,响应会被延迟。
第二,长列表虚拟滚动。如果列表一次性渲染 50 条以上数据,在鸿蒙低端机上滑动会有肉眼可见的掉帧。我换成了虚拟列表组件,只渲染可视区域内的节点。实测在 200 条数据的场景下,滚动流畅度提升非常明显。
第三,图片懒加载与压缩。首页 banner 图不能直接丢一张 2MB 的原图上去,我在上传服务端时统一压缩到 webp 或 jpg,质量 80%,宽度按设备 2 倍屏适配。这个优化收益最直接,页面加载速度几乎能提升一倍。
5. 打包签名与上架应用市场
5.1 从 HBuilderX 到 DevEco Studio 的整包打包
开发完成后,最关键的一步就是把代码变成可以被应用市场接受的正式包。在 HBuilderX 里点击“发行”菜单,选择鸿蒙 Next 打包,编译器会生成一个鸿蒙工程。重点来了:这个工程不是最终的发布包,它还需要用 DevEco Studio 打开并完成正式签名,才会生成可以上架的应用文件。
签名的具体流程是:在华为开发者后台创建一个应用,拿到应用的 App ID,然后生成公私钥证书和 Profile 描述文件。在 DevEco Studio 的签名配置界面里,把这些信息填进去,Build 时选择 Release 模式,系统会自动完成签名和打包。
这里有一个特别容易踩的坑:HBuilderX 里配置的 manifest.json 中的应用 ID,必须和 DevEco Studio 里签名用的应用 ID 完全一致,一个字符都不能差。我第一次打包时把应用 ID 填错了一个下划线,导致构建出来的包安装时报签名无效,排查了一个小时才找到问题。
5.2 上架流程与审核材料清单
上架鸿蒙应用市场需要提前准备的材料,比想象中要多,我整理了一份清单:
华为开发者账号(个人开发者即可,需实名认证)
应用基本信息:名称、简介、关键词、分类
应用图标:要求高清、无圆角,建议准备 1024x1024
应用截图:通常需要 3-5 张,要求包含主要功能界面,分辨率要按上架要求生成
软件著作权证书:如果软件是自主研发,建议申请软著,审核更快
测试账号说明:如果应用有登录功能,需要向审核人员提供测试账号。
提交审核页面在 AppGallery Connect 后台,创建应用后依次填写信息、上传包。整个上传界面很清晰,按步骤走就行,需要注意的是“隐私政策”栏目,如果只填一个网页链接,审核人员手动访问打不开,大概率会被驳回。
5.3 审核驳回的常见原因与避坑
我身边几个朋友上架鸿蒙应用,被驳回的原因高度集中在以下几类:
权限声明与实际功能不符。这是最大的一类驳回原因。比如你声明了读取位置权限,但应用里根本没有地点相关功能。所以打包前把 manifest 里的权限再检查一遍,该删的删。
隐私政策链接无法访问。
截图上显示的是开发数据。审核人员看不到真实业务内容,会觉得应用不完整。上架截图里的数据一定要真实、干净,不要用“假数据测试”这样的文案。
应用内出现“测试版”“Beta”字样。部分审核严格时,带测试字样的应用会被认为未完成。
避坑建议:提交审核前,自己按审核人员的视角走一遍完整流程:注册登录、看功能、测试权限弹窗、查隐私政策链接。发现问题先自行处理后,再审一次,通过率会高很多。
6. 常见问题速查表与个人体会
6.1 高频问题排查速查表
| 问题 | 可能原因 |
|---|
解决思路
|
| 运行到鸿蒙提示找不到 SDK | HBuilderX 未配置 DevEco Studio 的 SDK 路径 |
|---|
在 HBuilderX 设置里填写 SDK 路径,确保版本匹配
|
| 页面白屏 | 路由配置不对或入口文件挂了 |
|---|
检查 pages.json 首页路径、main.js 是否有语法错误
|
| uni.request 请求失败 | 接口不是 HTTPS 或证书不受信 |
|---|
切换 HTTPS 接口,确认证书链完整
|
| 真机安装失败 | 调试证书未配置或应用 ID 不一致 |
|---|
在 DevEco Studio 里重新配置签名,确认 ID 与 manifest 一致
|
| 上架审核被驳回 | 权限与功能不符、隐私政策不可访问 |
|---|
|
这个速查表是我在整套开发流程里真实遇到过的场景。最让我意外的是“应用 ID 不一致”这个问题,它不会在开发阶段暴露,只要是打正式包就必然出现,而新手第一时间根本想不到是签名配置的锅。
6.2 跑通第一版后的几点个人体会
整个项目从零到上架,我做下来最大的感受是:技术难度其实没有想象中大,真正花时间的是处理各种工具链衔接和审核规范。uni-app 本身已经把 90% 的跨端复杂度隐藏掉了,剩下的 10% 集中在鸿蒙编译链路和上架环节。
如果你正准备起步,我的建议是从一个小工具应用开始,比如一个计算器、一个汇率查询、一个待办清单。先把“HBuilderX 写页面 → 编译成鸿蒙工程 → DevEco Studio 签名 → 真机运行 → 打包上架”这一整条链路跑通一次,再去做复杂业务。
最后分享一个我在调试时的小技巧:在 App.vue 的 onLaunch 里,把系统版本、设备型号、屏幕宽度这些参数打一条日志,整理成一个对象存到全局。后面所有页面遇到兼容性问题时,先看这个对象里的参数,再决定是否要做条件判断。这个习惯帮我少走了不少弯路,也让我在追查线上问题时多了一条判断依据。
