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/7/19 14:49:04阅读更多 →
IDELAYE2

IDELAYE2

输入固定或可变延迟的元件每个输入/输出模块都包含一个可编程的绝对延迟元件IDELAYE2。该元件既可连接至输入寄存器/ISERDES2,也可直接驱动FPGA逻辑电路。IDELAYE2采用31抽头环形延迟结构。引脚描述PortdirectionwidthfunctionCinput1所有控制输入(RST,C…

2026/7/19 14:47:04阅读更多 →
C2000 eQEP模块全解析:正交编码器信号处理与宽速域电机速度估算实战

C2000 eQEP模块全解析:正交编码器信号处理与宽速域电机速度估算实战

1. 项目概述:从编码器信号到电机速度的闭环在工业自动化、机器人、数控机床这些高精度运动控制领域,工程师们最核心的挑战之一,就是如何实时、精确地“感知”电机的转动。电机转了多少度?转速是快是慢?方向是正转还是反…

2026/7/19 14:47:04阅读更多 →
拒绝单机版幻觉:Hermes 如何让 AI 编程从“个人爽文”变成团队基建

拒绝单机版幻觉:Hermes 如何让 AI 编程从“个人爽文”变成团队基建

聊《Hermes真能提效吗?先看流程里最慢的那一步》之前,先说一句实在的:别急着背概念,先看它在真实项目里到底解决什么问题。摘要先把这篇文章的目标说清楚:看完之后,你应该能判断这件事值不值得做&#xff0…

2026/7/19 22:42:53阅读更多 →
鸿蒙Flutter SizedBox与ConstrainedBox尺寸约束:固定尺寸与范围约束

鸿蒙Flutter SizedBox与ConstrainedBox尺寸约束:固定尺寸与范围约束

作者:高红帆(Math_teacher_fan) 仓库地址:https://gitcode.com/feng8403000/FlutterfromBeginnertoAdvancedForHarmonyOS.git 联系邮箱:372699828qq.com 引言 在Flutter开发中,控制组件尺寸是构建布局的基…

2026/7/19 22:42:53阅读更多 →
从 ETL 到 RAG:大数据工程师转型 LLM 时的“过度设计”陷阱与实战取舍

从 ETL 到 RAG:大数据工程师转型 LLM 时的“过度设计”陷阱与实战取舍

聊《做过大数据的人学大模型,哪些经验可以直接迁移?》之前,先说一句实在的:别急着背概念,先看它在真实项目里到底解决什么问题。 摘要 先把这篇文章的目标说清楚:看完之后,你应该能判断这件事…

2026/7/19 22:42:53阅读更多 →
出海孟加拉商贸数字化复盘:港口跨境进销存、多仓下沉铺货与纺织配套落地实践

出海孟加拉商贸数字化复盘:港口跨境进销存、多仓下沉铺货与纺织配套落地实践

孟加拉国作为南亚核心滨海商贸国家,依托吉大港、蒙格拉两大核心港口、恒河三角洲物流网络与超1.7亿人口消费红利,形成了以纺织服装产业配套、日用百货进口分销、五金建材基建供货、下沉市场多级铺货为核心的特色商业业态。当地制造业基础薄弱&#xff0c…

2026/7/19 22:42:53阅读更多 →
【学习】Java打印HelloWord(图片代码)

【学习】Java打印HelloWord(图片代码)

Paper/csdn/【学习】Java打印HelloWorld/【学习】Java打印HelloWorld.md at master BProbie/Paperhttps://github.com/BProbie/Paper/blob/master/csdn/%E3%80%90%E5%AD%A6%E4%B9%A0%E3%80%91Java%E6%89%93%E5%8D%B0HelloWorld/%E3%80%90%E5%AD%A6%E4%B9%A0%E3%80%91Java%E6%8…

2026/7/19 22:42:53阅读更多 →
如何轻松批量下载B站视频?BilibiliDown完整使用指南

如何轻松批量下载B站视频?BilibiliDown完整使用指南

如何轻松批量下载B站视频?BilibiliDown完整使用指南 【免费下载链接】BilibiliDown (GUI-多平台支持) B站 哔哩哔哩 视频下载器。支持稍后再看、收藏夹、UP主视频批量下载|Bilibili Video Downloader 😳 项目地址: https://gitcode.com/gh_mirrors/bi/…

2026/7/19 22:40:52阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/19 0:01:04阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/19 0:01:04阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/19 0:01:04阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/19 0:01:04阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/19 0:01:04阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/19 0:01:04阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/18 22:49:46阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/19 14:50:26阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/19 18:50:36阅读更多 →