uni-app 打通鸿蒙踩坑日记:从迷茫到上架的完整心路历程
缘起:为什么选择鸿蒙
作为一名前端开发者,我一直在关注各种新兴的技术生态。当华为宣布鸿蒙系统将独立发展时,我就意识到这可能是下一个重要的技术风口。但真正促使我动手的,还是实际的需求痛点。
我们团队有一个成熟的 uni-app 项目,已经在 iOS 和 Android 上稳定运行了两年。但随着华为设备市场份额的不断扩大,越来越多的用户反馈希望有鸿蒙版本。看着应用商店里竞品陆续上线鸿蒙版本,我决定亲自趟一趟这趟"浑水"。
从今年 3 月开始,我花了近三个月时间,完成了从技术调研到实际上架的完整过程。今天就来分享这段充满挑战又收获满满的经历。
技术选型:为什么坚持 uni-app
在开始之前,团队内部有过激烈的讨论:是重新开发原生鸿蒙应用,还是基于现有 uni-app 项目进行适配?
重新开发原生鸿蒙的优势:
- 性能最优,体验最佳
- 可以充分利用鸿蒙的特有能力
- 技术栈更纯粹
但最终我们还是选择了 uni-app 适配,原因很现实:
- 开发成本:团队熟悉 Vue 技术栈,重新学习 ArkTS 成本太高
- 维护成本:三套代码意味着三倍的维护工作量
- 迭代速度:业务需求变化快,需要快速响应
- DCloud 的承诺:官方明确表示会持续加强鸿蒙支持
事后证明,这个选择是正确的,但过程远比想象中曲折。
环境搭建:第一个坑就栽了跟头
按照官方文档,我信心满满地开始环境搭建。HBuilderX、DevEco Studio、Node.js,一切看起来都很简单。
第一个坑:模拟器选择
文档说"下载 API 19 模拟器即可",但我下载后始终无法启动。各种错误提示看得我头皮发麻:
模拟器启动失败:硬件加速未开启
VT-x 不可用,请检查 BIOS 设置
我花了整整两天时间:
- 重启电脑进入 BIOS 开启虚拟化
- 在 Windows 功能中启用 Hyper-V
- 更新显卡驱动
- 重装 DevEco Studio
最后发现是杀毒软件冲突。关闭某数字卫士后,模拟器终于正常启动了。
经验教训:环境问题不要死磕,及时换思路。后来我直接使用真机调试,效率反而更高。
项目迁移:看似顺利的开始
将现有 uni-app 项目迁移到鸿蒙,最初进展出乎意料的顺利。
基础页面适配
大部分 Vue 页面无需修改即可运行,uni-app 的跨端能力确实令人印象深刻:
<template>
<view class="container">
<text class="title">{{ title }}</text>
<button @click="handleClick">点击我</button>
</view>
</template>
<script>
export default {
data() {
return {
title: 'Hello HarmonyOS'
}
},
methods: {
handleClick() {
uni.showToast({
title: '点击成功'
})
}
}
}
</script>
<style>
.container {
padding: 20px;
}
.title {
font-size: 18px;
color: #333;
}
</style>
路由配置
pages.json的配置也完全兼容:
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页"
}
},
{
"path": "pages/detail/detail",
"style": {
"navigationBarTitleText": "详情页"
}
}
]
}
静态资源
static目录下的图片、字体等资源都能正常加载。前 80% 的工作在两天内就完成了,我甚至觉得鸿蒙开发不过如此。
但接下来的 20%,花了我 80% 的时间。
深水区:平台特定适配
导航栏差异
第一个平台差异出现在导航栏。在 iOS 和 Android 上正常的导航栏,在鸿蒙上出现了错位。
问题现象:
- 标题位置偏移
- 返回按钮点击区域异常
- 状态栏颜色不匹配
解决方案:
在 pages.json中为鸿蒙平台单独配置:
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页",
"app-plus": {
"titleNView": {
// iOS/Android 配置
}
},
"harmony": {
"navigationBarBackgroundColor": "#FFFFFF",
"navigationBarTextStyle": "black",
"navigationBarTitleText": "首页",
"navigationStyle": "default"
}
}
}
]
}
组件兼容性问题
更大的挑战来自组件兼容性。我们项目中使用了大量第三方组件,有些在鸿蒙上表现异常。
案例:图片裁剪组件
在 iOS/Android 上正常的图片裁剪组件,在鸿蒙上出现以下问题:
- 触摸事件响应错乱
- 裁剪框位置计算错误
- 性能卡顿明显
排查过程:
- 首先怀疑是触摸事件机制差异
- 尝试修改事件处理逻辑,效果有限
- 深入组件源码,发现使用了 DOM API
- 鸿蒙不支持部分 DOM API
最终方案:
寻找替代组件,选择明确支持鸿蒙的图片裁剪库。这个过程让我意识到:不是所有 web 生态都能无缝迁移到鸿蒙。
API 兼容性
uni-app 的 API 在鸿蒙上的支持程度不一:
完全兼容的:
uni.showToastuni.requestuni.getStorageuni.chooseImage
需要适配的:
uni.getSystemInfo返回的信息结构不同uni.onKeyboardHeightChange在鸿蒙上触发机制不同- 部分设备 API 需要鸿蒙特定实现
不支持的:
- 某些平台特定的扩展 API
我们建立了兼容性检查清单,对每个 API 进行测试:
// 兼容性封装示例
export const getSafeArea = () => {
return new Promise((resolve, reject) => {
uni.getSystemInfo({
success: (res) => {
// 鸿蒙平台特殊处理
if (res.platform === 'harmony') {
resolve({
top: res.statusBarHeight || 0,
bottom: 0,
left: 0,
right: 0
})
} else {
resolve(res.safeArea)
}
},
fail: reject
})
})
}
性能优化:从卡顿到流畅
首次在真机上运行应用时,性能问题让我震惊:列表滚动卡顿、页面切换白屏、内存占用过高。
列表性能优化
问题:商品列表页有 1000+ 项目,滚动时严重卡顿。
分析:
- 鸿蒙的列表渲染机制与 web 不同
- 大量 DOM 节点导致内存压力
- 图片加载没有做优化
解决方案:
- 虚拟列表:
<template>
<view class="list-container">
<unicloud-db
ref="udb"
collection="goods"
where="category_id == categoryId"
orderby="create_time desc"
:page-size="20"
@load="onListLoad"
v-slot:default="{data, loading, error}">
<virtual-list
:data="data"
:item-size="100"
key-field="_id">
<template v-slot:default="{item}">
<goods-item :item="item" />
</template>
</virtual-list>
</unicloud-db>
</view>
</template>
- 图片懒加载:
<image
:src="item.image"
mode="aspectFill"
lazy-load
:fade-show="false"
@load="onImageLoad"
@error="onImageError">
</image>
- 内存管理:
// 监听页面生命周期
onPageScroll(e) {
// 可视区域外的图片取消加载
this.checkVisibleImages()
},
onHide() {
// 页面隐藏时释放大资源
this.clearCache()
}
启动速度优化
问题:应用冷启动需要 3-5 秒,体验较差。
优化措施:
- 代码分包:
// pages.json
{
"subPackages": [
{
"root": "pagesA",
"pages": [
"page1/page1",
"page2/page2"
]
},
{
"root": "pagesB",
"pages": [
"page3/page3",
"page4/page4"
]
}
]
}
- 预加载关键资源:
// app.vue
onLaunch() {
// 预加载首屏必要数据
this.preloadEssentialData()
// 异步加载非关键资源
setTimeout(() => {
this.preloadSecondaryResources()
}, 1000)
}
- 减少同步操作:
// 优化前:同步阻塞
const userInfo = uni.getStorageSync('userInfo')
const settings = uni.getStorageSync('settings')
// 优化后:异步并行
Promise.all([
this.getStorage('userInfo'),
this.getStorage('settings')
]).then(([userInfo, settings]) => {
// 处理数据
})
经过优化后,启动时间缩短到 1-2 秒,列表滚动帧率稳定在 60fps。
打包发布:证书的"坑爹"之旅
如果说代码适配是技术挑战,那证书和打包就是流程挑战。
证书配置的坑
第一个坑:证书类型混淆
我一开始把调试证书当成发布证书使用,结果打包时各种错误:
错误: 证书类型不匹配
错误: 签名验证失败
解决方案:
- 调试证书:用于开发阶段真机调试
- 发布证书:用于应用商店上架
- 两者不能混用
第二个坑:证书有效期
发布证书有一年有效期,但测试证书只有三个月。我第一次提交审核时,证书差点过期。
经验:设置证书到期提醒,提前一个月更新。
打包流程优化
手动打包效率低下,我建立了自动化流程:
// package.json 脚本
{
"scripts": {
"build:harmony:debug": "uni build --platform harmony --mode development",
"build:harmony:release": "uni build --platform harmony --mode production",
"pack:harmony": "node scripts/pack-harmony.js",
"deploy:harmony": "npm run build:harmony:release && npm run pack:harmony"
}
}
// scripts/pack-harmony.js
const { execSync } = require('child_process')
const fs = require('fs')
const path = require('path')
console.log('🚀 开始鸿蒙应用打包...')
// 检查构建产物
const buildPath = path.join(__dirname, '../dist/build/harmony')
if (!fs.existsSync(buildPath)) {
console.error('❌ 构建产物不存在,请先执行构建')
process.exit(1)
}
// 执行鸿蒙特定打包逻辑
try {
execSync('node scripts/harmony-specific-pack.js', { stdio: 'inherit' })
console.log('✅ 鸿蒙应用打包完成')
} catch (error) {
console.error('❌ 打包失败:', error)
process.exit(1)
}
上架审核:意料之外的拒绝
第一次提交审核时,我信心满满,觉得经过充分测试的应用肯定能一次通过。结果被打脸了。
审核拒绝原因:
- 隐私政策不完整缺少数据存储说明未说明第三方 SDK 数据收集
- 权限说明不清晰相机权限用途描述过于简单位置权限必要性未充分说明
- 应用截图不规范截图包含状态栏时间"17:30"部分截图尺寸不一致
解决方案:
隐私政策完善:
## 数据存储说明
本应用使用本地存储保存用户偏好设置,使用华为云存储备份用户数据。所有数据均加密存储。
## 第三方 SDK 说明
- 华为账号 SDK:用于用户登录认证
- 华为推送 SDK:用于消息推送
- 微信分享 SDK:用于内容分享
权限说明重写:
## 相机权限
用途:用于扫描商品条形码、拍摄商品照片
必要性:核心功能需要,无法替代
数据处理:图片仅在本地处理,不上传服务器
## 位置权限
用途:用于推荐附近门店、配送地址定位
必要性:增值功能需要,非核心功能
数据处理:位置信息加密传输到服务器
截图规范:
- 使用模拟器统一截图
- 隐藏状态栏敏感信息
- 确保所有截图尺寸一致
- 展示核心功能流程
第二次提交后,顺利通过审核。
监控与反馈:上架只是开始
应用上架后,真正的挑战才开始。用户反馈和线上监控发现了我们测试中未发现的问题。
线上问题排查
案例:特定机型闪退
有用户反馈在华为 Mate 40 上频繁闪退,但我们测试机上正常。
排查过程:
- 查看 AGC 崩溃日志
- 发现内存溢出错误
- 定位到图片加载组件
- 特定机型内存管理更严格
解决方案:
// 图片加载优化
loadImage(url) {
return new Promise((resolve, reject) => {
const img = new Image()
// 限制图片尺寸
if (this.isLowMemoryDevice()) {
url = this.addImageSizeParams(url, '800x600')
}
img.onload = () => {
// 及时释放引用
img.onload = null
img.onerror = null
resolve(img)
}
img.onerror = reject
img.src = url
})
}
用户反馈处理
建立用户反馈响应机制:
- 24 小时内响应严重问题
- 每周汇总用户反馈
- 每月发布优化版本
经验总结与建议
技术层面
- 组件选择要谨慎优先选择官方组件验证第三方组件兼容性准备备用方案
- 性能优化要前置开始就考虑性能问题建立性能监控体系定期进行性能回归测试
流程层面
- 证书管理要规范区分调试和发布证书设置证书到期提醒备份证书文件
- 审核准备要充分仔细阅读审核指南准备完整的说明材料提前测试审核流程
- 监控体系要完善建立崩溃监控收集用户反馈定期分析使用数据
给后来者的建议
- 不要畏惧鸿蒙开发uni-app 已经提供了很好的支持大部分代码可以复用社区资源越来越丰富
- 从小功能开始尝试先移植简单页面逐步增加复杂度积累经验再处理核心功能
- 善用官方资源DCloud 文档很详细华为开发者社区活跃官方技术支持响应快
- 加入开发者社区学习他人经验分享自己的收获共同推动生态发展
未来规划
鸿蒙版本的成功上线,给了我们很大信心。下一步计划:
- 深度集成鸿蒙特性原子化服务探索分布式能力应用鸿蒙特有 UI 交互
- 性能持续优化启动速度优化到 1 秒内内存占用降低 30%功耗优化
- 多端协同与 iOS/Android 版本功能同步数据互通体验优化统一的设计语言
写在最后
三个月的鸿蒙适配之旅,让我深刻体会到:技术转型虽然痛苦,但收获远超预期。
最大的收获不是技术上架,而是思维转变:从"web 思维"到"多端思维",从"功能实现"到"体验优化"。
鸿蒙生态还在快速发展,现在入局正是好时机。uni-app 为跨端开发提供了很好的基础,让我们能够以较低成本探索新平台。
如果你也在考虑鸿蒙开发,我的建议是:不要观望,直接开始。从简单的 demo 开始,逐步深入,你会发现鸿蒙开发没有想象中那么难。
在这个过程中,你会遇到各种问题,但也会收获解决问题的成就感。更重要的是,你将成为新生态的早期参与者,这本身就是宝贵的机会。
希望这篇文章能帮助到正在或准备进行鸿蒙开发的你。如果遇到问题,欢迎交流讨论,我们一起推动鸿蒙生态的繁荣!
1 个评论
要回复文章请先登录或注册
小怪乖