HarmonyOS 多端实战:把手机 UI 搬上大屏后,我翻车了
HarmonyOS 一次开发、多端部署实战:Phone、Pad、PC、Watch 到底该共享什么
最开始做 Nameo 多端适配时,我不是“差点”把 Phone 页面直接搬到大屏——我真的搬了,而且它还真的跑起来了。
然后尴尬也跟着跑来了。
Phone 那一小块布局安静地待在屏幕正中间,左右两边空得十分讲礼貌,谁也不挨着谁。到了设置页,内容在目标窗口里看起来大约只占六成,剩下的地方仿佛在提醒我:这是一块很大的屏幕,而你只带来了一颗很小的心。
那一刻我脑子里只有一个问题:
如果用户把应用装到 Pad 或 PC 上,看到的只是“手机版远距离观赏模式”,那他安装大屏版本的意义是什么?
“一次开发,多端部署”最容易制造的误会,就是把“一次开发”听成“一张页面到处拉伸”。如果 Ctrl+C 真能解决多端开发,架构师可能早就转行卖键盘了。
这篇不做 ArkUI API 大全。我想拿 Nameo 的 Phone、Pad、PC、Watch 实际拆分讲清楚五件事:设备职责怎么定、代码共享到哪一层、什么时候共用 HAP、什么时候独立拆包,以及“能构建”和“真机跑过”到底差多少。
一、“一次开发”的“一”,不是一张 UI
HarmonyOS 官方把“一多”拆成工程组织、功能兼容和界面适配几个层次。翻译成项目里的大白话就是:
- 工程尽量是一套;
- 数据模型、存储、网络和业务能力尽量共享;
- 到了产品体验层,允许每种设备诚实地不一样。
所以 Nameo 当前并不是“四个端就复制四个工程”,也不是“一张 UI 按屏幕比例缩放”。它的真实拓扑是:
4 种产品体验,3 个产品 HAP
Phone HAP ── phone + tablet
PC HAP ── 2in1
Wearable ── wearable
根据真实配置脱敏简化后,三个入口的差异就是:
// Phone entry
deviceTypes: ['phone', 'tablet']
// PC entry
deviceTypes: ['2in1']
// Wearable entry
deviceTypes: ['wearable']
Phone 和 Pad 共用一个 HAP,但不共用一张页面;PC 和 Watch 因为任务、交互方式、产品能力差异更大,使用独立产品入口。
如果你刚从 Java/Vue 转过来,可以先这样理解:HAP 是应用的部署包单元,HAR 是静态共享包;这里说的 feature,是按业务能力拆出的模块,Shell 则是一端页面的导航与布局外壳。
共享的是“会做什么”,产品层决定“在这台设备上怎么做才不别扭”。
图里画的是主干,不是一条必须逐层穿过的流水线:products 会按需依赖 features 和 common,features 也会依赖 common。

二、我不是先分屏幕,而是先分四种用户时刻
多端规划如果从“手机多少寸、平板多少寸”开始,很容易最后只剩一张响应式尺寸表。Nameo 先问的是:用户为什么在这一刻拿起这台设备?
| 端 | 用户时刻 | 主要职责 | 坚决不直接搬过去的东西 |
|---|---|---|---|
| Phone | 信息刚出现,随手记一下 | 语音、拍照、文件、分享采集,即时问答和轻量待办 | PC 多栏管理、长时间批量编辑 |
| Pad | 坐下来阅读、整理或开会 | 列表+详情、会议工作区、知识整理和筛选 | 只把 Phone 页面横向拉宽 |
| PC | 办公桌面批量处理 | 多工作区、文件采集、会议、知识与待办重操作 | 手机底部导航和一层层返回 |
| Watch | 抬腕几秒钟确认或补记 | 摘要、待办、闪卡、知识速览、短输入 | 长文编辑、复杂表单和多层导航 |
这些职责不是先画一张漂亮表格再往项目里套,而是从大屏空旷、导航上下文丢失、PC 专属工作流和 Watch 圆屏限制等实际问题倒推出来的。Phone 解决“别让信息跑掉”;Pad 解决“上下文别总被新页面顶走”;PC 解决“我现在要坐下来认真干活”;Watch 解决“抬腕看一眼、点一下、说一句”。
让手表编辑五千字会议纪要,不叫“一次开发,多端部署”,叫对手腕实施职场霸凌。
三、Phone 和 Pad 为什么共用 HAP,却不共用 Shell
Nameo 的 Phone entry 同时声明了 phone 和 tablet。窗口创建或尺寸变化后,工程会把像素换算成 vp,再根据 600vp、840vp 两个项目断点写入布局状态:
Window size
↓
BreakpointSystem
↓
compact / regular / expanded
↓
Phone Shell / PadAdaptiveShell
这里有一个特别容易写错的判断:断点判断的是当前窗口,不是设备身份证。
我关心的不是“你是不是折叠屏”,而是“你现在展开到了什么窗口形态”。按窗口断点的设计,宽折叠、双折叠、三折叠在不同展开状态下可能进入不同布局;窄窗口继续走 Phone 形态,达到 regular/expanded 后,信息结构才切成 Pad 工作台。这里说的是布局策略,不代表当前版本已经验收了所有折叠型号。
脱敏后的核心分支其实很短:
const mode = updateBreakpoint(windowWidthVp)
if (mode === 'compact') {
PhoneShell()
} else {
PadAdaptiveShell()
}
这也是我们没有急着再拆一个独立 Tablet HAP 的重要原因。未来达到 regular/expanded 窗口的折叠形态,本身就需要复用 Pad 布局。如果 Phone、Tablet 过早分成两套产品,后面同一套适配要维护两遍;同一 bundle 下再塞一个 Tablet entry,还会带来安装、共存和测试成本。算完这笔账,我决定先不拆,而不是为了目录看起来更“多端”就多造一个包。
但“共用 HAP”绝不等于“Pad 是大号 Phone”。
Phone 上打开知识卡,可以进入独立详情页;Pad 上打开同一张卡,左侧列表应该保留,只更新右侧详情。Phone 上页面跳转是合理导航,Pad 上如果每点一次都把工作区整个顶走,大屏就只剩“屏幕大,迷路也更远”。
四、设置页把我教育了:大屏不是把 width 改成 100%
第一次直接搬 Phone 设置页时,最刺眼的问题就是中央内容太小、两边太空。但修复它也不是把每一行强行拉满整块屏幕。
如果“AI 模式”开关从左边横跨到右边,信息之间的关联反而会变弱。
Nameo 后来的 Pad 设置不再沿用手机的长列表,而是单独做了设置工作区:账号中心、声纹、系统设置、关于页各自进入工作区;登录资料与知识统计可以组成双栏;设置项按成对的两列重新组织;内容区还设置最大宽度,避免在超宽窗口里无限拉长。
注意,这里的关键不是某个固定百分比。设置页“约占六成”只是我当时看到的现象,不是 HarmonyOS 规范。真正可复用的方法是:
窄窗口:接近全宽,保证可用
中宽窗口:开始重排,单栏变双栏
大窗口:限制内容宽度,给结构留白,而不是给空白留白

留白本身没有罪。没有信息结构的留白,才会让用户觉得“这应用只是被放大了”。
五、Nameo 为什么最终让 PC 独立拆包
看到这里可能有人会问:既然 Phone/Pad 可以共用,PC 为什么不继续共用?
对 Nameo 来说,决定因素不是多了一种设备,而是 PC 的任务、窗口、交互和功能演进已经与移动端明显分叉。
Nameo 的 PC 不只是多一个侧栏。它有独立的桌面工作台和自由窗口处理;AI 通话收起后是能在窗口内拖动、带边界约束的浮条;会议有列表/详情和录制控制/实时转写的双栏结构;文件采集要承接系统选择、待处理文件和批量入库。
这些都不是在 Phone 页面外面包一层 if (isPC) 就能体面解决的。
我一开始也舍不得拆。毕竟代码都写了,能共用一行是一行。但当产品层的判断越来越像春运售票窗口——Phone 走左边、Tablet 走右边、2in1 请上二楼——所谓复用就已经变成大家挤在同一间屋子里互相打扰。
所以 PC 使用独立产品 HAP,但这不意味着全部重写。PC 的页面、路由、窗口和交互独立,待办、知识、会议和认证等领域能力仍然依赖共享模块。
一句话概括:
PC 拆的是产品体验,不是把业务底座也复制一份。
六、feature 到底拆多细?我的标准不是“看起来高级”
Nameo 的工程边界可以简化成三层。注意这里不是一条必须逐层经过的单行流水线:产品层会同时依赖 feature 和 common,feature 也会依赖 common。
products
├─ phone:Phone 页面 + Pad adaptive 工作台
├─ pc:桌面窗口、导航和 PC 交互
└─ wearable:圆屏、表冠和轻动作
├────→ features:todo / knowledge / chat / me / meeting
└────→ common:模型、数据库、KV、网络、认证、底层服务
features ─→ common
common 不是“暂时不知道放哪儿”的代码收容所,features 也不是为了目录好看而切得越碎越好。
我现在判断拆分力度,会连续问三个问题:
- 这段代码在不同端解决的是不是同一个业务问题?
- 它们变化的原因是不是相同?
- 抽出来以后,产品层会不会反而塞满端类型判断?
待办的读取、完成状态持久化、知识卡更新,可以共享;Pad 的筛选工作区、PC 的分组列表、Watch 的紧凑字段投影,不该硬共享。变化原因相同的代码才值得放到一起,不是长得像就必须住一间房。
七、同一份数据,为什么仍会在不同端出问题
多端架构图画完以后,最容易产生一种错觉:既然 ViewModel 和 Repository 共用了,四端状态就会自动整齐得像军训方阵。
现实当然没有这么配合。
同一条待办在领域层有相同语义:读取、勾选、持久化、同步。但到了产品层:
- Pad 会投影成待处理/已处理与类型筛选;
- PC 会按普通、紧急、逾期重新分组;
- Watch 只保留标题、待办文本和完成按钮等紧凑字段。
共享 ViewModel 更新,不等于这些端侧二次数组一定自动刷新。上一篇《Java 程序员第一次看 ArkTS:像老乡,但口音有点重》里详细复盘过的“PC 待办勾选后数量变了、列表不动”,本质上就是这一类问题:数据已经变了,界面仍然持有旧快照。
最后的修复不是放弃共享,而是把边界说清:领域层尽量收住业务真相,产品层负责生成自己的展示投影;不能指望对旧数组原地改两下,框架就能自动理解你的良苦用心。Nameo 目前仍在渐进收紧这条边界,并不是已经完成了教科书式的纯 MVVM。
这也是多端项目比 Demo 更值钱的地方:难点往往不在“能不能调用 API”,而在共享状态经过不同页面结构、导航和生命周期后,还能不能保持一致。
对 AI 应用也是同一条逻辑:Phone/Watch 更像轻量采集入口,Pad/PC 更适合整理和深度操作;共享层维持知识与待办语义,RAG 负责从知识中找到依据,Agent 再决定要不要拆任务、调工具。多端页面不应该各自复制一套 AI 业务真相。
八、Watch 不是缩小版 Phone,KV 同步也不是写一行 put
Watch 最先逼我承认的一件事是:通用组件能画出来,不代表它适合圆屏。
Nameo 的 Watch 首页采用纵向 5 屏 ArcSwiper,承载用户信息、今日摘要、知识速览、待办和闪卡。我没有继续拿普通 Swiper 硬撑。ArcSwiper 面向弧形轮播,平台能力覆盖触摸、导航点和表冠等切页方式,其中表冠响应还依赖组件获焦;Nameo 当前先用它解决五屏内容在圆形视口中的组织问题,具体真机动作只按后面的专项证据陈述。组件换成圆屏专用形态之后,UI 才像手表应用,而不是有人拿剪刀把手机页面裁成了一个圆。
更麻烦的其实是登录同步。
Watch 不提供一套独立登录页,而是由 Phone 通过分布式 KV 同步登录状态,Watch 启动后读取,再恢复本地账号资料。先把安全边界说在前面:这里只能放最小必要状态或受保护的短期凭据,绝不能把长期认证信息明文塞进去。下面只讲初始化和补发时序,不会展示真实认证字段。至于怎样过期、撤销和解绑,还得按正式安全方案设计,不能把这段实现照搬成万能登录方案。架构图上看起来只有几根箭头:
Phone 登录
→ 写入认证 KV
→ 分布式同步
→ Watch 读取
→ 恢复登录状态
真机第一次却出现过“手机明明已经登录,手表没反应”。根因不是 KV 不会同步,而是时序:Phone 的认证服务先恢复了登录态,分布式同步服务还没初始化好;登录回调只发布一次,失败以后就没人再管。Watch 初装时又读得太早,于是两边都很努力,刚好彼此错过。
Watch 端对应的恢复逻辑,根据真实代码脱敏后大致如下:
await syncService.init(context)
await authService.init(context)
await authService.loadAuthFromKvStore()
syncService.setAuthChangeCallback(() => {
authService.loadAuthFromKvStore()
})
scheduleAuthReload(2500)
scheduleAuthReload(8000)
后来的实现补了几层兜底:
- Phone 在同步服务初始化完成后重新发布当前登录态;
- Phone 发现远端设备或重新回到前台时再次补发;
- Watch 监听登录状态 key 变化;
- Watch 启动后和回前台进行少量延迟重读。

登录状态数据和知识卡数据会分开处理,退出登录也有单独标记。补发与重读必须保持幂等,正式生产还要处理版本、过期、撤销和旧值覆盖;延迟重读只是兜底,不是一致性协议。时序跑通也不等于安全工作结束,最小权限、凭据保护和设备解绑还得单独设计。
这段代码不如架构图优雅,但它很真实:多设备的上线顺序不可控,不能把“发过一次”当成“对方一定收到”。
这些不是纸上设计。我在 HUAWEI WATCH 5 上把摘要、速记、待办、知识速览、闪卡和设置都完整走过,后来又拿 Watch 5 和 Mate 60 Pro 专门联调了分布式 KV 登录、用户名和头像同步。也正是这次真机联调,才让我碰到了前面那个“手机已经登录,手表却没反应”的时序问题。
九、同一个 HAP,还是独立 HAP?我现在只问五个问题
以后再做多端技术选型,我不会先问“能不能复用”,而是按顺序问:
- 不同设备上的核心任务是否相同?
- 页面结构和主要交互是否相似?
- 自适应伸缩、折行或响应式断点能否解决?
- 输入方式、窗口生命周期和系统能力是否已经明显不同?
- 强行共享后,产品层的条件分支是否开始破坏可读性和测试成本?
对应的决策大致是:
任务相同、结构相似
└─ 同一 HAP + 自适应
任务相同、结构会随窗口改变
└─ 同一 HAP + 响应式 Shell
任务、交互、窗口和功能范围明显分叉
└─ 产品层独立,必要时独立 HAP
Phone/Pad 是第二种;PC 和 Watch 更接近第三种。
这三行表示主要策略,不是互斥的 API 清单。同一页面经常会先在断点处切换结构,再用拉伸、隐藏、折行等能力完成局部自适应。
但这不是所有项目的标准答案。一个只展示新闻列表的 PC 端,可能完全没必要独立 HAP;一个重度生产力 Pad 应用,也可能最终值得拆开。架构选择必须跟着产品职责和未来重点走,不是照着 Nameo 的目录抄作业。

十、正式复盘
这次多端拆分最终让我确定了四条原则:
- 先定义每种设备上的用户任务,再讨论页面与代码复用。
- 断点服务于窗口,不要用设备名称代替布局判断。
common/features共享业务语义,products尊重页面、导航、输入和生命周期差异。- 多设备同步不能只在登录回调里“发一次就算了”,还要处理服务初始化、设备上线和回前台的时序。
复用率不是越高越好。真正应该放在一起的,是那些因为同一个原因而变化的代码。
这篇解决的是“同一套能力应该在哪台设备上怎样出现”。下一篇,我们继续往 AI 深水区走:聊天机器人、RAG、Workflow、Agent 到底差在哪儿?Nameo 为什么在六月准备提审时,又因为一个多跳问题返工?
这次不聊“接个大模型 API 就算 AI 应用”,聊企业真正敢用的 RAG 和 Agent 边界。
延伸阅读
- HarmonyOS 官方《一次开发,多端部署概览》
- HarmonyOS 官方《多设备工程部署与发布》
- HarmonyOS 官方《响应式布局》
- HarmonyOS 官方《创建弧形轮播(ArcSwiper)》
- HarmonyOS 官方《电脑应用开发》
- HarmonyOS 官方《智能穿戴应用开发》
更多推荐


所有评论(0)