HarmonyOS:ArkTS数据模型的设计与演进实战
引子数据设计好了UI 就是套模板星空运势应用经历了两次数据结构的变化。第一版是四个文件——Types.ts、SignsData.ts、TarotData.ts、Constants.ts每个文件职责单一。重构后合并成了一个 DataModel.ets12 星座的数据从 5 个字段扩展到 8 个加了优势、弱点、最佳配对运势数据从两行文字扩展到四维评分幸运物。改动的动机很简单数据量变大了文件太多反而麻烦——每次改星座数据要打开 SignsData.ts改运势要打开另一个文件改颜色又要打开 Constants.ts。合并成一个文件后所有数据定义在一个地方改起来方便。完整效果数据模型的完整结构DataModel.ets 里定义了六个核心类和四个常量数组DataModel.ets ├── class Pos2D # 坐标点 ├── class StarPt # 星点坐标大小 ├── class SignData # 星座完整数据 ├── class TarotInfo # 塔罗牌数据 ├── class FortuneDetail # 运势维度数据 ├── class LuckyItem # 幸运物数据 ├── const SIGNS: SignData[] # 12 星座数据 ├── const TAROT: TarotInfo[] # 21 张塔罗牌 ├── const WHEEL_COLORS # 12 种转盘颜色 ├── const FORTUNE_LABELS # 12 个运势标签 └── const LUCKY_ITEMS # 4 个幸运物为什么合并成一个文件重构前拆成四个文件的理由是单一职责——每个文件只做一件事。但实际开发中发现数据量不大总共也就 200 多行代码拆成四个文件每个只有几十行反而增加了文件切换成本数据关联紧密SignData 依赖 StarPtFortuneDetail 依赖 LuckyItem拆开后 import 路径复杂修改频率高星座数据和运势数据经常一起改放在一个文件里更方便合并的原则是数据量小且关联紧密时单文件比多文件更高效。六个核心类的设计Pos2D最简单的坐标点classPos2D{x:number0;y:number0;constructor(x:number,y:number){this.xx;this.yy;}}只有 x 和 y 两个属性用于转盘的星座图标定位和星图的星点定位。为什么不用系统自带的 PointArkUI 没有内置的 Point 类。如果用{ x: number, y: number }对象每次都要写类型注解。用class可以通过new Pos2D(x, y)快速创建实例代码更简洁。StarPt带大小的星点classStarPt{x:number0;y:number0;sz:number0;constructor(x:number,y:number,sz:number){this.xx;this.yy;this.szsz;}}比 Pos2D 多一个szsize属性用于控制星点的大小。sz的值是 2-5渲染时乘以 2.5 映射到字体大小5px-12.5px。为什么 sz 不直接存像素值存原始尺寸比如 5、7.5、10更直观但存 2-5 的小数字更紧凑。渲染时再乘以系数方便统一调整——如果要让所有星点变大只需改系数不需要改 12 个星座的数据。SignData星座的完整信息classSignData{name:string;// 名称emoji:string;// 符号date:string;// 日期范围elem:string;// 元素trait:string;// 特质stars:StarPt[][];// 星点坐标strengths:string;// 优势weaknesses:string;// 弱点matches:string[][];// 最佳配对constructor(n:string,e:string,d:string,el:string,t:string,s:StarPt[],str:string,wk:string,mt:string[][]){this.namen;this.emojie;this.dated;this.elemel;this.traitt;this.starss;this.strengthsstr;this.weaknesseswk;this.matchesmt;}}这是最核心的数据类包含了星座的所有信息。9 个字段分三类类别字段用途基础信息name, emoji, date, elem, trait星座网格、详情标签可视化数据stars星点渲染富文本数据strengths, weaknesses, matches详情卡片为什么用位置参数而不是对象参数构造函数用了 9 个位置参数而不是传一个对象// 位置参数当前实现newSignData(白羊座,♈,3.21-4.19,火,热情勇敢,[...],充满活力,冲动急躁,[狮子座])// 对象参数另一种方式newSignData({name:白羊座,emoji:♈,date:3.21-4.19,elem:火,trait:热情勇敢,stars:[...],strengths:充满活力,weaknesses:冲动急躁,matches:[狮子座]})位置参数的优点是写起来短缺点是参数顺序不能错。对象参数的优点是参数顺序随意缺点是写起来长。对于这种数据量固定、很少修改的场景位置参数更高效。如果字段经常增减对象参数更安全。默认参数的作用后三个参数strengths、weaknesses、matches有默认值空字符串和空数组。这样在定义星座数据时如果某个星座暂时没有详细信息可以省略这些参数newSignData(测试座,☆,1.1-1.31,火,测试,[...])// strengths、weaknesses、matches 自动为空TarotInfo塔罗牌数据classTarotInfo{name:string;emoji:string;key:string;text:string;constructor(n:string,e:string,k:string,t:string){this.namen;this.emojie;this.keyk;this.textt;}}四个字段名称、emoji、关键词、解读文本。比 SignData 简单得多因为塔罗牌不需要星点坐标和优劣势。FortuneDetail运势维度数据classFortuneDetail{category:string;emoji:string;stars:number0;text:string;constructor(c:string,e:string,s:number,t:string){this.categoryc;this.emojie;this.starss;this.textt;}}用于运势详解的四维评分维度名称、图标、星级1-5、解读文本。LuckyItem幸运物数据classLuckyItem{emoji:string;label:string;value:string;constructor(e:string,l:string,v:string){this.emojie;this.labell;this.valuev;}}用于今日幸运物图标、标题、具体内容。四个常量数组的设计SIGNS12 星座数据constSIGNS:SignData[][newSignData(白羊座,♈,3.21-4.19,火,热情勇敢·行动力强,[newStarPt(100,30,3),newStarPt(60,70,2),...],充满活力勇于开拓天生的领导者,冲动急躁缺乏耐心容易半途而废,[狮子座,射手座]),// ... 共 12 个];每个星座 9 个参数12 个星座总共 108 个参数。数据量不小但都在一个数组里维护方便。TAROT21 张塔罗牌constTAROT:TarotInfo[][newTarotInfo(愚者,,新开始·冒险,新的旅程即将开始...),newTarotInfo(魔术师,,创造力·技巧,你拥有实现目标的所有资源...),// ... 共 21 张];21 张大阿尔卡纳每张 4 个参数。数据量比星座少但每张牌的解读文本较长。WHEEL_COLORS12 种转盘颜色constWHEEL_COLORS:string[][#7B4FBF,#3B6FC2,#20A39E,#E89240,#C44569,#5D5FEF,#2E9C7A,#D4594C,#6B5CE7,#3D8FD1,#18A085,#E07030];12 种颜色和 12 个星座一一对应。颜色选择考虑了对比度——在深色背景上都能看清。FORTUNE_LABELS12 个运势标签constFORTUNE_LABELS:string[][大吉,中吉,小吉,末吉,大吉,中吉,小吉,大吉,中吉,末吉,小吉,中吉];12 个运势标签和 12 个星座一一对应。运势结果由转盘停止位置决定不是随机的。LUCKY_ITEMS4 个幸运物constLUCKY_ITEMS:LuckyItem[][newLuckyItem(,幸运色,蓝色),newLuckyItem(,守护石,青金石),newLuckyItem(,吉时,下午4点),newLuckyItem(,方位,北方),];4 个固定幸运物不随星座变化。如果要做更精细的运势可以让不同星座有不同的幸运物。数据如何驱动整个应用整个应用的 UI 完全由数据驱动SIGNS → 转盘的星座图标ForEach wheelPos → 星图的星座网格ForEach Grid → 星图的详情卡片selectedSign 索引 TAROT → 塔罗的洗牌结果tarotIdx 索引 → 塔罗的牌面显示ForEach tarotOpen WHEEL_COLORS → 转盘的背景色ForEach backgroundColor FORTUNE_LABELS → 转盘的运势标签resultIdx 索引 LUCKY_ITEMS → 运势详解的幸运物ForEach修改任何数据对应的 UI 自动更新。这就是数据驱动的核心思想。数据流向的单向性数据流是单向的数据 → UI没有反向流UI → 数据除非用户交互触发状态变化。这种单向数据流让代码更可预测——看到数据就知道 UI 长什么样看到 UI 就知道数据是什么。数据的不可变性常量数组SIGNS、TAROT 等用const声明不可修改。如果要修改数据必须创建新数组// 错误const 数组不能修改SIGNS[0].name新名字;// 正确创建新数组constnewSIGNS[...SIGNS];newSIGNS[0]newSignData(新名字,...);不可变性的好处是避免意外修改——如果某个组件不小心改了 SIGNS 数据会影响所有使用 SIGNS 的组件。从多文件到单文件的演进过程第一版四个文件model/ ├── Types.ts # 类定义 ├── SignsData.ts # 星座数据 ├── TarotData.ts # 塔罗数据 └── Constants.ts # 颜色、运势标签优点每个文件职责单一修改一个不影响另一个。缺点文件切换频繁import 路径复杂。第二版两个文件model/ ├── DataModel.ts # 类定义 常量数据 └── index.ts # 统一导出优点减少文件切换import 更简洁。缺点DataModel.ts 还是有点大。第三版当前一个文件model/ └── DataModel.ets # 所有数据定义优点所有数据在一个文件里改起来最方便。缺点文件有点大200 行但还在可接受范围内。演进的判断标准什么时候该合并什么时候该拆分判断标准是数据量300 行可以合并500 行应该拆分修改频率经常一起改的数据应该合并很少同时改的数据可以拆分关联性关联紧密的数据应该合并独立的数据可以拆分当前 DataModel.ets 大约 200 行在可接受范围内。如果以后加更多星座比如 24 星座或更多塔罗牌78 张完整牌组可能需要再拆分。踩坑记录坑 1类的默认值必须在声明时赋值classSignData{name:string;// 必须在声明时赋值// name: string; // 错误编译器会报错}ArkTS 要求类的属性在声明时赋默认值不能只声明类型。这是因为 ArkTS 编译后会生成 JavaScript 代码如果没有默认值属性会是undefined。坑 2const 数组的深拷贝问题consta[{x:1},{x:2}];constb[...a];// 浅拷贝b[0].x100;// a[0].x 也会变成 100展开运算符[...]只做浅拷贝数组里的对象还是同一个引用。如果要深拷贝需要用 JSON 序列化constbJSON.parse(JSON.stringify(a));但当前应用的数据都是只读的不需要深拷贝。坑 3类的构造函数参数顺序构造函数有 9 个参数顺序不能错。如果记不住顺序可以看 IDE 的参数提示或者用对象参数代替。坑 4export 的写法ArkTS 的 export 语法和 TypeScript 略有不同// TypeScriptexportclassSignData{...}// ArkTSclassSignData{...}export{SignData};如果在类声明前加export编译器可能会报错。代码改进建议1. 数据的外部化如果星座数据经常变动比如从网络获取可以考虑把数据放到外部文件JSON 或数据库运行时加载。这样修改数据不需要重新编译。2. 类型的强化当前类的属性都是string或number没有更精确的类型。比如elem只能是 ‘火’ | ‘土’ | ‘风’ | ‘水’可以用枚举或联合类型typeElement火|土|风|水;3. 数据的校验当前数据没有校验——如果某个星座的 stars 数组为空渲染时会出错。可以加校验逻辑constructor(...){if(s.length0){console.warn(${n}has no stars);}// ...}4. 数据的可扩展性如果以后要支持自定义星座或编辑运势需要把数据存到本地。可以用 Preferences API 或关系型数据库。总结DataModel.ets 是整个应用的数据基础——转盘的旋转结果、星图的星座详情、塔罗的洗牌结果都依赖这里的定义。数据结构设计好了UI 就是套模板数据结构没设计好UI 怎么改都别扭。适用边界这个部分适合用作 ArkTS 数据模型设计的学习案例涵盖了类定义、常量数组、构造函数、数据驱动、文件组织等核心知识点。但如果要上架应用商店还需要补充数据校验、类型强化、外部化存储、可扩展性设计等内容。建议在此基础上逐步扩展而不是一次性做完所有功能。

相关新闻

TMS320F2838x DMA通道优先级配置与高优先级模式实战解析

TMS320F2838x DMA通道优先级配置与高优先级模式实战解析

1. 项目概述:为什么DMA优先级配置是嵌入式开发者的必修课在嵌入式系统开发,尤其是基于TMS320F2838x这类高性能实时微控制器的项目中,直接内存访问(DMA)模块的性能直接决定了整个系统的实时性和效率。很多工程师在初次接…

2026/10/8 8:38:47 阅读更多 →
Free95:打造开源Windows兼容操作系统的终极指南

Free95:打造开源Windows兼容操作系统的终极指南

Free95:打造开源Windows兼容操作系统的终极指南 【免费下载链接】Free95 Free95 is an open-source windows-compatible operating system. 项目地址: https://gitcode.com/gh_mirrors/fr/Free95 Free95是一款创新的开源Windows兼容操作系统,它为…

2026/10/10 17:23:52 阅读更多 →
输入一个 URL 以后,一次 HTTP 事务经历了什么

输入一个 URL 以后,一次 HTTP 事务经历了什么

第02篇_输入一个 URL 以后,一次 HTTP 事务经历了什么[!abstract] 用 http://192.168.1.20:8088/api/status 跟踪同一笔事务:URL 怎样拆成连接参数与消息参数,请求怎样到达 PLC,响应又怎样回到 Client。适合谁收藏第一次系统理解 H…

2026/10/13 14:07:26 阅读更多 →

最新新闻

OpenCode 终端 AI 编程代理:从安装配置到实战避坑指南

OpenCode 终端 AI 编程代理:从安装配置到实战避坑指南

1. 从一个终端窗口说起:OpenCode 到底在解决什么问题第一次听说 OpenCode 的人,多半是在某个技术群里看到别人贴了一张终端截图——黑底绿字,光标闪烁,敲几行命令就能让 AI 帮忙改代码、跑测试、查文档。很多人第一反应是"这…

2026/10/15 2:00:12 阅读更多 →
虚拟线程与常见第三方开源库的兼容性避坑清单:数据库连接池、HttpClient 与序列化

虚拟线程与常见第三方开源库的兼容性避坑清单:数据库连接池、HttpClient 与序列化

在 Spring Boot 3 工程中,开启虚拟线程(Virtual Threads)只需要轻飘飘的一行配置: spring.threads.virtual.enabledtrue很多技术负责人看到官网宣传的“平替传统线程、百万并发无压力”,便以为只要把这一行配置打上&am…

2026/10/15 2:00:12 阅读更多 →
专科生必备:9款降AI率工具实测与选型指南

专科生必备:9款降AI率工具实测与选型指南

1. 项目背景与核心价值2026年的职场环境对专科生的技术素养提出了更高要求。随着AI工具在工作场景中的渗透率突破60%,掌握降AI率工具已成为专科生提升就业竞争力的关键技能。这类工具能有效降低工作流程中的AI依赖度,帮助从业者保持独立思考能力和核心业…

2026/10/15 2:00:12 阅读更多 →
搜索热词“superpowers”拆解:安装前必须搞清的增强工具与模组逻辑

搜索热词“superpowers”拆解:安装前必须搞清的增强工具与模组逻辑

1. 当“superpowers”成为一个搜索热词,它到底在指什么“superpowers”这个词最近在搜索框里出现的频率明显高了起来,连带“想要安装superpowers”这样的长尾词也冒了出来。第一次看到这个组合的人大概率会愣一下:这到底是一个软件、一个插件…

2026/10/15 2:00:12 阅读更多 →
Web安全入门:读懂Web世界里的攻防

Web安全入门:读懂Web世界里的攻防

Web安全入门:读懂Web世界里的攻防一、什么是Web安全二、Web安全为什么很重要三、Web常见漏洞简介(OWASP Top10核心)四、Web攻击的基本思路五、Web安全防护思路六、Web安全学习方向结尾一、什么是Web安全 Web安全,全称Web应用安全…

2026/10/15 2:00:12 阅读更多 →
Gitea局域网服务器Local: No connection could be made because the target machine actively refused it.

Gitea局域网服务器Local: No connection could be made because the target machine actively refused it.

现象:使用Gitea 当git仓库管理A电脑当服务器,B电脑推送,有lfs大文件A电脑可以自己推送。B电脑推送失败:提示git.exe push --progress -- "192" master:master warning: use of unencrypted HTTP remote URLs is not re…

2026/10/15 1:59:12 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →