2026-09-24 深夜,我们把 zjchina 的定位从「内容站架构方法论」升级为「协作认知架构方法论」。 24 小时后,鸿蒙版工程骨架落地——这是定位升维后的第一场实战。 而在本文上线前夜,一次「APP 没有刷新键」的小修复,意外做成了未来多机协同「事件去重」的第一个原型。 本文记录这场实战的技术细节、踩坑过程与决策逻辑,作为 ADR 0004 的注脚。
一、为什么鸿蒙是「协作认知架构」的第一块试金石
ADR 0004 把 zjchina 的定位从「内容站」升维到「协作认知架构」——简单说,就是从「做好一个网站」变成「做好一套能连接物理世界的系统」。
鸿蒙适配恰好踩中三个维度:
| 维度 | 鸿蒙场景 | 我们的机制 |
|---|---|---|
| 空间可达 | 工厂大屏、导购机器人触屏、无人店收银台 | ArkWeb 薄壳 + 服务端渲染(零 JS 首屏) |
| 时间可验证 | 交易记录可回溯、决策过程可审计 | 事件日志(类似飞行记录仪)+ 内容指纹链 |
| 边界生长 | 传感器(RFID/重量)、防截屏、全屏展示 | 平台能力隔离(鸿蒙特有代码单独存放) |
鸿蒙不是「又一个平台」,而是协作认知架构向物理世界延伸的第一个触点。
二、技术方案:ArkWeb 薄壳,不改现有架构
2.1 三轨道不变,鸿蒙只是多一个「容器」
我们的网站有三条渲染轨道(类似三条生产线):
┌─────────────────────────────────────────────────────────────┐
│ 内容轨道(首页、文章页、决策日志) │
│ 服务端直接生成 HTML,无需下载 JS,打开即读 │
│ → 鸿蒙浏览器直接打开,零改动 │
├─────────────────────────────────────────────────────────────┤
│ 应用轨道(后台管理) │
│ 浏览器下载 wasm(编译后的代码),交互复杂 │
│ → 鸿蒙浏览器可运行,但非最优 │
├─────────────────────────────────────────────────────────────┤
│ 壳轨道(桌面/手机 App) │
│ 原生窗口包裹网页内容 │
│ → 鸿蒙 H1 = ArkWeb 薄壳(第一个原生容器) │
│ → 鸿蒙 H3 = 原生交易页(未来) │
└─────────────────────────────────────────────────────────────┘
关键决策:鸿蒙不改变三条轨道,而是给「壳轨道」增加一种交付形态。内容页面(服务端渲染)在鸿蒙浏览器里零改动运行——这是「中立契约层」的第二次兑现(第一次是服务端渲染/客户端渲染双端同源)。
2.2 窗口级能力:防截屏与全屏
防截屏(鸿蒙 API 26 的 setWindowPrivacyMode):
- 截屏/录屏时窗口自动变黑,无需修改网页代码(系统在窗口层拦截)
- 坑位:API 26 需要特殊权限(
ohos.permission.PRIVACY_WINDOW,系统级授权)- 设计文档假设「无需权限」已过时
- 工厂场景(企业证书)可用;公开版若拿不到授权则通过配置关闭
- 降级策略:如果权限不足,catch 错误继续加载(不阻断使用,但日志留痕)
全屏沉浸(setWindowLayoutFullScreen):
- 内容延伸至状态栏/导航栏,工厂大屏场景必需
- 待实测:安全区边距(
env(safe-area-inset-*))在鸿蒙浏览器是否正确传递
2.3 工程骨架:Stage 模型,零依赖
harmony/
├── AppScope/app.json5 # 应用标识:net.zjchina.shell
├── build-profile.json5 # API 26.0.0, 签名配置
├── entry/src/main/
│ ├── module.json5 # 防截屏权限声明
│ ├── ets/entryability/EntryAbility.ets # 防截屏+全屏逻辑
│ ├── ets/pages/Index.ets # ArkWeb 壳(加载网页)
│ ├── ets/model/AppConfig.ets # 配置中心(开关集中管理)
│ └── resources/ # 字符串/颜色/页面配置
└── scripts/harmony-build.sh # 一键构建(环境变量注入)
关键设计:
- AppConfig.ets:所有窗口能力开关集中管理(防截屏/全屏/网址),避免魔法数字散落各处
- harmony-build.sh:构建时注入环境变量(SDK 路径等),不污染全局 shell 配置
- 零依赖:
oh-package.json5无第三方依赖,纯鸿蒙原生代码 + 系统浏览器
三、踩坑记录:API 26 vs 设计文档
| 坑位 | 设计文档假设 | 实际情况(API 26) | 解决方案 |
|---|---|---|---|
| 防截屏权限 | 无需权限 | 需系统级权限(PRIVACY_WINDOW) | 声明权限,catch 错误降级 |
| 错误回调签名 | isMainFrame 直接参数 | isMainFrame 在 event.request 对象上 | 按官方类型定义修正 |
| 版本号格式 | "26"(数字) | "26.0.0"(字符串) | 按构建工具错误提示修正 |
| API 26 运行环境 | 下载本地镜像即可调试 | 当时 DevEco Studio 的本地模拟器列表中没有 API 26 镜像(镜像清单随 IDE 版本更新,以实际列表为准);在线模拟器虽有 API 26,但命令行 hdc install 装本地签名包被拒 | DevEco Run 直连 Pura X 在线模拟器,云端签名部署 |
这个坑的关键不在「下载」,而在「安装通道」:在线模拟器运行在云端,本地命令行工具因签名体系不一致无法把应用装进去;而 DevEco Run 走云端通道,自动完成构建、签名、部署一条龙,直接把应用送上 Pura X 在线模拟器(API 26)。所以验收全部跑在 API 26 在线模拟器上——防截屏是全功能生效而非降级;唯一未闭环项是云端环境拿不到 hilog(即上方清单第 8 项)。
四、跨语言边界:让「契约」当裁判
鸿蒙适配是「跨语言交互问题」的第三次出现(前两次:C++ 后端 ↔ Rust、Rust ↔ 浏览器 wasm)。
纪律不变:
- 边界只传契约,不传调用:鸿蒙代码(ArkTS)与 Rust 核心之间只传数据类型(JSON 格式),与浏览器→后端、服务端渲染→后端是同一批类型
- 漂移锁从三向扩四向:现在数据格式校验是「Rust 计算 + 文档定义 + C++」三向锁定;鸿蒙加入后,ArkTS 侧类型从统一的数据规范自动生成——校验机制不变,只是多一个使用方
- 平台能力仍走通道隔离:碰鸿蒙系统功能(传感器/窗口/防截屏)的代码永远住在鸿蒙特有目录,核心逻辑保持纯净可编译到鸿蒙,不出现平台判断代码污染
H2 验证(后续):只暴露核心逻辑的一个纯函数(如 Markdown 渲染),鸿蒙代码调用,同一测试用例双端断言——不做 UI。
五、对网页编排的影响:零影响
内容页面(首页、文章页、决策日志)服务端渲染,鸿蒙浏览器直接打开,无需任何改动。
唯一需要实测的假设,验收也给出了结论:
- 鸿蒙浏览器是否支持 Service Worker?(设计文档「默认支持」此前只是假设)
- 若支持:离线缓存加速 + 断网兜底(显示友好离线页)
- 若不支持:服务端渲染仍可用(只是没缓存加速),后台管理页不开放(wasm 依赖缓存)
验收清单与结果:
- 部署方式:
命令行 hdc install→ DevEco Run 部署(在线模拟器绕过本地安装限制)✅ - 防截屏(截屏时窗口黑屏)✅ API 26 全功能生效
- 全屏(内容延伸至安全区)✅
- 离线缓存(在线首访/二访/断网)✅(网络连接活跃)
- 内页跳转(文章详情页)✅(鸿蒙浏览器加载网站)
- 动画性能(wasm 粒子效果)✅(渲染进程独立,内存占用合理)
- 服务端渲染首屏(零 JS 可读)✅(鸿蒙浏览器直接消费 HTML)
- 稳定性(无错误日志)⚠️(在线模拟器日志权限限制)
合计 7/8 通过(Pura X 在线模拟器,API 26):防截屏全功能生效;唯一未闭环项是云端环境拿不到 hilog 日志,留待真机或本地模拟器补验。
六、上线前夜:APP 没有刷新键,事件驱动替代轮询
准备发布本文时发现一个真实问题:Service Worker 为了让二次访问秒开,会先展示本地缓存、再在后台静默更新。浏览器里用户还能手动刷新,可页面装进鸿蒙壳之后——APP 没有刷新键,新文章可能一直不出现。
6.1 第一反应是轮询,但它被否决了
直觉方案很简单:页面每隔几十秒请求一次「版本号」,发现变化就弹提示。否决理由同样简单:博客的发布间隔可能长达数周,定时请求里 99.99% 都是「没有变化」——为了一次发布,让每个访客的设备持续空转耗电,这笔账不划算。APP 在后台时连接还会被系统冻结,轮询并不可靠。
6.2 「发布」本来就是可以监听的事件
重新审视机制后发现,浏览器早就准备好了答案:
- 每次发版,构建脚本都会重新生成 Service Worker 脚本
sw.js——版本号、资源清单、完整性指纹全部写进它的字节,且这个文件本身被设置为永不缓存; - 浏览器在每次打开页面时自动比对它:字节变了,就安装新 SW,页面随之收到一个
updatefound(发现更新)事件; - 于是判定收敛成一个极简状态机:首次安装静默接管(新访客看到的本就是最新内容,不该被打扰);页面已被旧版本控制时再收到更新事件,才弹出横幅,并且同一次更新只弹一次。
对于没有导航的场景——APP 切到后台再切回来、网络重新连通、从系统页面缓存恢复——则在对应的用户行为事件里主动检查一次。全程没有任何定时器:发布间隔再长,通知成本都是零。
用户点「立即刷新」时,新 SW 已经完成接管并清空旧缓存,这一下刷新必然拿到新内容,不会在新旧版本之间反复横跳。
6.3 这是未来多机协同「事件去重」的第一个原型
这个功能看似只是个更新提示,架构上却特意做了一件事:通知入口与触发源解耦。横幅和更新动作并不关心「是谁发现了新版本」——Service Worker 的更新事件只是第一个信号源;将来股票行情、无人店多机协作的实时事件,经过 SSE/Push 这样的长连接通道,可以直接触发同一个通知入口,UI 与更新逻辑一行都不用改。
核心判定只有两个状态——「是否已被旧版本接管」「本次会话是否已提示」——保证同一事件只打扰一次。这与多机协同里「同一事件只处理一次」的幂等去重是同一个问题:去重必须做在事件入口,而不是等消息堆到界面上再去过滤。状态机用 10 组测试向量锁死,包括「首次访问不提示」「同一更新不重复提示」「事件乱序到达也不误报」。
也诚实标注边界:这个机制保证「下次打开页面或 APP 回到前台时」立即通知,而不是挂机不动时秒级弹窗。真正的秒级推送(长连接/系统推送)是下一阶段的能力——接缝已经留好。
这篇文章的上线,就是它的第一次线上实测:如果你正从旧版本打开本站,页面顶部此刻应该已经出现「发现新版本,内容已更新」。
七、下一步:从「能跑」到「好用」
H2(后续):鸿蒙代码调用 Rust 核心逻辑(如 Markdown 渲染),验证数据格式四向锁定。
H3(未来):鸿蒙原生交易页(结算/支付/订单确认),核心逻辑在 Rust,UI 双份(鸿蒙原生 + 网页版)。
实时通道(接缝已留):新版本横幅的通知入口与触发源解耦,下阶段接 SSE/Push 时直接复用——行情跳动、多机协作事件与「内容更新」走同一个入口、同一套幂等去重。
5.1 无人店:鸿蒙 H1 是「导购机器人触屏」的技术预演——鸿蒙浏览器承载商品讲解(内容轨道),鸿蒙原生页承载交易(壳轨道),传感器数据经通道入事件日志。
八、结语:协作认知架构的第一块砖
鸿蒙 H1 不是「又一个平台适配」,而是协作认知架构向物理世界延伸的第一块砖。
它验证了:
- 三轨道不变:内容/应用/壳的路由表、渲染模式、降级链
- 中立契约层:内容页面零改动跑在鸿蒙浏览器
- 通道隔离:平台能力(防截屏/全屏/传感器)住在鸿蒙特有目录,核心逻辑保持纯净
- 可观测降级:低版本或权限不足的设备上,防截屏按设计走 catch 降级(不阻断加载);API 26 在线模拟器全功能生效(低版本降级路径尚未在真机实测)
上线前夜补的事件驱动更新提醒,则验证了另一件更面向未来的事:事件可以从任何通道来,但入口只有一个、同一事件只处理一次——这条纪律先在最不起眼的「更新提示」上跑通,等它的是行情事件流和多机协作。
下一块砖:股票模拟盘的「乐观执行 + 资金事件流」,然后是无人店的「多机协作」。
参考文献:
- ADR 0004:协作认知架构定位升维(docs/decisions/0004-collaborative-cognition-architecture.md)
- 鸿蒙策略预研(docs/superpowers/specs/2026-09-25-harmony-support-strategy.md)
- H1 落地状态(docs/superpowers/specs/2026-09-25-harmony-h1-status.md)
- 页面编排要求(docs/superpowers/specs/2026-09-25-harmony-page-layout-requirements.md)