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/30 9:32:54 阅读更多 →
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/7/28 21:14:35 阅读更多 →
输入一个 URL 以后,一次 HTTP 事务经历了什么

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

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

2026/7/29 10:44:36 阅读更多 →

最新新闻

Java工厂模式深度解析:从简单工厂到抽象工厂的实战应用

Java工厂模式深度解析:从简单工厂到抽象工厂的实战应用

1. 项目概述:为什么我们需要工厂模式? 干了这么多年Java开发,每次带新人或者面试的时候,聊到设计模式,工厂模式总是绕不开的话题。它不像单例模式那样简单直接,也不像策略模式那样充满“智慧”,…

2026/7/30 9:32:50 阅读更多 →
抖音直播数据采集技术深度解析:DouyinLiveWebFetcher架构设计与实现方案

抖音直播数据采集技术深度解析:DouyinLiveWebFetcher架构设计与实现方案

抖音直播数据采集技术深度解析:DouyinLiveWebFetcher架构设计与实现方案 【免费下载链接】DouyinLiveWebFetcher 抖音直播间网页版的弹幕数据抓取(2025最新版本) 项目地址: https://gitcode.com/gh_mirrors/do/DouyinLiveWebFetcher 抖…

2026/7/30 9:32:50 阅读更多 →
Java远程调试实战:JDWP协议原理、IDEA配置与生产环境安全指南

Java远程调试实战:JDWP协议原理、IDEA配置与生产环境安全指南

1. 项目概述:为什么我们需要远程Debug? 想象一下这个场景:你负责维护的一个核心Java服务,在测试环境跑得好好的,一上线到生产服务器就间歇性报错,日志里只有一句模糊的“NullPointerException”&#xff0c…

2026/7/30 9:32:50 阅读更多 →
LangChain Prompt 工程:从基础模板到企业级实践

LangChain Prompt 工程:从基础模板到企业级实践

1. LangChain Prompt 核心概念解析 在构建大语言模型应用时,Prompt(提示词)设计是连接人类意图与AI理解的关键桥梁。LangChain作为当前最流行的LLM应用开发框架,其Prompt模块提供了远超基础文本提示的工程化能力。实际开发中&…

2026/7/30 9:32:50 阅读更多 →
蓝桥杯C++真题精讲:从求和、等差数列到灌溉的算法实战

蓝桥杯C++真题精讲:从求和、等差数列到灌溉的算法实战

1. 项目概述:从“求和”到“灌溉”,一次蓝桥杯真题的深度实战最近在带学生备赛蓝桥杯,特别是C B组,发现很多同学对真题的练习还停留在“看答案”的阶段,缺乏对题目背后逻辑的深度拆解和举一反三的能力。正好手头有2024…

2026/7/30 9:32:50 阅读更多 →
STM32 USB DFU固件升级实战:从原理到配置与避坑指南

STM32 USB DFU固件升级实战:从原理到配置与避坑指南

1. 为什么需要USB下载?从串口到USB的升级之路如果你玩过一阵子STM32,最开始接触程序下载的方式,大概率是串口。找一根USB转TTL线,接上BOOT0和BOOT1引脚,用FlyMCU或者STM32CubeProgrammer的串口模式,一通操作…

2026/7/30 9:31:50 阅读更多 →

日新闻

Windows驱动存储终极清理工具:DriverStoreExplorer完全指南

Windows驱动存储终极清理工具:DriverStoreExplorer完全指南

Windows驱动存储终极清理工具:DriverStoreExplorer完全指南 【免费下载链接】DriverStoreExplorer Driver Store Explorer 项目地址: https://gitcode.com/gh_mirrors/dr/DriverStoreExplorer 您是否曾因Windows系统盘空间不足而烦恼?是否遇到过设…

2026/7/30 0:00:13 阅读更多 →
如何3步掌握Video Download Helper:网页视频下载的完整实战指南

如何3步掌握Video Download Helper:网页视频下载的完整实战指南

如何3步掌握Video Download Helper:网页视频下载的完整实战指南 【免费下载链接】VideoDownloadHelper Chrome Extension to Help Download Video for Some Video Sites. 项目地址: https://gitcode.com/gh_mirrors/vi/VideoDownloadHelper 你是否曾经在浏览…

2026/7/30 0:00:13 阅读更多 →
“双减”后首个AI备课压力测试报告:覆盖32所中小学的176节AI辅助课,暴露4大隐性增负节点

“双减”后首个AI备课压力测试报告:覆盖32所中小学的176节AI辅助课,暴露4大隐性增负节点

更多请点击: https://intelliparadigm.com 第一章:AI 教师备课辅助 AI 教师备课辅助系统正逐步成为教育数字化转型的核心支撑工具,它并非替代教师,而是通过语义理解、知识图谱与多模态生成能力,将教师从重复性劳动中解…

2026/7/30 0:00:13 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/7/29 22:18:20 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/29 14:34:28 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/7/29 15:00:03 阅读更多 →

月新闻