最近把我的饮水记录App从纯移动端方案重构了一版,目标很直接:一套基于 React Native 的代码,尽量无缝跑进鸿蒙设备。项目里涉及饮水进度计算、快速补水记录、补水场景选择、操作反馈四个核心模块,过程中踩了不少跨平台适配的坑,尤其被 react native 启动白屏和循环滚轮选择器反复折磨过。这篇就把完整思路、关键实现和鸿蒙适配里的实操细节拆开讲清楚,给正在做同类跨平台鸿蒙应用的团队一个可参考的样本。
先说背景。这款App最初只在安卓和iOS上跑,今年鸿蒙设备保有量上来之后,团队开始评估要不要为鸿蒙单独开发一套。评估下来发现:纯ArkTS重写一次成本不低,还要维护两套业务逻辑,后续迭代容易分裂。更关键的是,饮水记录这类工具型应用的业务复杂度不高,核心页面就是首页进度、快捷记录、历史统计,UI形态相对固定,用跨平台方案完全够用。所以最终决定走React Native鸿蒙化这条路,把已有代码迁移过去,同时针对鸿蒙端交互习惯做了不少细节调整。
1. 这个应用到底在做什么:需求拆解与方案取舍
1.1 饮水记录看似简单,难在高频和低打断
如果你没用过这类App,我先描述一下核心使用场景。用户一天可能要记录8到12次饮水,每次动作不超过3秒,打开App、点一下补水量、放回口袋。这种高频低打断的产品形态最忌讳复杂流程,所以界面上不能有任何多余输入。饮水进度计算背后也不是简单加一杯水,它涉及每日目标设置、体重联动、天气与运动修正、跨天重置等一系列状态处理。
另一个容易被忽略的点是:这些记录数据不是孤立存在的。用户可能会在手机上记录,也期望数据能同步到手表或平板上。虽然第一版只做单设备,但数据模型一开始就得设计成“将来可以多端同步”的结构,否则后患无穷。这也是我为什么要单独把数据模型拎出来设计的原因,因为它直接决定了后续进度计算、统计报表、场景分析能不能顺畅扩展。
1.2 为什么选React Native做鸿蒙跨平台
鸿蒙应用开发目前可选路径大致有三条:一是纯ArkTS原生开发,性能和系统能力调用最彻底;二是Flutter跨平台方案,社区适配也在推进;三是React Native鸿蒙化,也就是我这版采用的方式。
选React Native的核心理由是业务代码复用率。项目本身在安卓/iOS端已经积累了大量业务代码,RN的JS层代码可以直接复用,只需要针对鸿蒙容器进行原生适配。Flutter虽然UI一致性好,但Dart侧的生态和现有JS资产不完全匹配,迁移时要把所有逻辑用Dart重新写一遍。相比之下,RN团队只要维护鸿蒙容器层和原生Module差异,业务层99%的JS代码可以原样保留,这个迁移成本优势太大了。
不过也要泼一盆冷水:RN鸿蒙方案至今不算“开箱即用”。很多热门的npm包并没有针对鸿蒙做原生适配,比如某些依赖安卓原生View的组件库就需要额外甄别。好在这类工具型App的UI组件集中,范围可控,把风险提前隔离掉,实际推进不会太难。
1.3 数据模型先想清楚,后续功能才好做
我把饮水记录拆成三个核心数据实体,这也是后续所有算法和交互的基础。
用户配置表存放每日目标饮水量、单次杯型容量、提醒时间窗、体重等参数。饮水记录表则是最核心的流水表,每条记录包含时间戳、补水量毫升数、场景类型(晨起、运动后、餐前、睡前等),这个场景字段是专门为后续统计报表设计的。每日汇总表则是按日期维度缓存总饮水量,用于首页进度条的快速渲染。
用伪代码表达大概是:
typescript复制type UserConfig = {
dailyTargetMl: number;
cupCapacityMl: number;
weightKg?: number;
remindStart?: string;
remindEnd?: string;
}
type WaterRecord = {
id: string;
timestamp: number;
amountMl: number;
sceneType: 'morning' | 'sport' | 'beforeMeal' | 'sleep' | 'custom';
note?: string;
}
type DailySummary = {
dateKey: string; // YYYY-MM-DD
totalMl: number;
recordIds: string[];
}
数据层设计最需要注意的一点是:不要把所有逻辑都放在组件里跑。我踩过的坑是早期把今日总量计算直接写在首页组件的state里,结果每次记录都触发全量重算,跨天时还要处理各种边界同步状态。后来改成“一条记录落库后再更新汇总表”的写模型,首页只是去读汇总值
