鸿蒙原生应用实战(五):塔罗牌App开发 — 数据模型、构建配置与工程优化

鸿蒙原生应用实战(五):塔罗牌App开发 --- 数据模型、构建配置与工程优化

前言

经过前四篇的逐步开发,我们已经完成了塔罗牌 App 的所有功能页面。本篇作为收官之作,将聚焦于那些"看不见但至关重要"的部分:

  • TarotData 全量数据模型:78 张塔罗牌的数据结构设计与内容填充
  • 资源文件管理:string / float / color 资源的定义与引用
  • 构建配置解析:build-profile / hvigor / module.json5 配置详解
  • API 版本适配:compatibleSdkVersion 与 targetSdkVersion 的策略
  • ArkTS 严格模式规则:常见编译错误及解决方案
  • 工程构建与发布:从代码到 HAP 包的完整流程

一、TarotData 数据模型深度解析

1.1 数据结构设计

塔罗牌分为两大类:

  • 大阿卡纳(Major Arcana):22 张(编号 0-XXI),代表重大人生课题
  • 小阿卡纳(Minor Arcana):56 张(分四花色 × 14 张),代表日常事务
typescript 复制代码
export interface TarotCard {
  id: number;           // 唯一标识(0-77)
  name: string;         // 中文名称(如"愚者""魔术师")
  englishName: string;  // 英文名称(如"The Fool""The Magician")
  arcana: string;       // 分类:'大阿卡纳' 或 '小阿卡纳'
  number: string;       // 编号(如'0'、'I'、'权杖一')
  keywords: string;     // 关键词(如"开始、冒险、天真")
  meaningUp: string;    // 正位含义
  meaningDown: string;  // 逆位含义
  description: string;  // 牌面描述
  color: string;        // 显示颜色(十六进制)
  isFavorite: boolean;  // 收藏状态(未使用,保留字段)
}

1.2 大阿卡纳数据示例

以下是部分大阿卡纳牌的数据(共 22 张,编号 0-XXI):

typescript 复制代码
export const TAROT_CARDS: TarotCard[] = [
  {
    id: 0,
    name: '愚者',
    englishName: 'The Fool',
    arcana: '大阿卡纳',
    number: '0',
    keywords: '开始、冒险、天真、无限可能',
    meaningUp: '愚者正位象征着全新的开始、天真的冒险精神和对未知的期待。这张牌鼓励你放下过去的包袱,勇敢地踏上新的旅程。它代表着一个充满可能性的新阶段,提醒你保持开放的心态和对世界的好奇心。',
    meaningDown: '愚者逆位暗示着鲁莽、不负责任的决策或对后果的忽视。你可能在逃避责任,或者在某个重要决定上过于草率。这张牌提醒你需要三思而后行,避免因冲动而后悔。',
    description: '愚者站在悬崖边缘,背上背着一个行囊,手中握着一朵白玫瑰,仰望天空。他即将迈出脚步,却完全不在意前方的危险。身边的小狗在吠叫,试图提醒他注意。画面中明亮的黄色背景象征着新的开始和无限的可能性。',
    color: '#F5E642',
    isFavorite: false
  },
  {
    id: 1,
    name: '魔术师',
    englishName: 'The Magician',
    arcana: '大阿卡纳',
    number: 'I',
    keywords: '创造力、技能、资源、显化',
    meaningUp: '魔术师正位代表着你有能力将想法转化为现实。你拥有所需的一切资源和技能,只需要善加运用。这张牌鼓励你相信自己的能力,主动采取行动去创造你想要的现实。',
    meaningDown: '魔术师逆位暗示着才能被浪费、操控他人或缺乏方向。你可能没有充分利用自己的天赋,或者正在用不正当的手段达到目的。需要重新审视自己的动机和方法。',
    description: '魔术师站在桌前,一只手举向天空接收能量,另一只手指向大地,象征着"如上如下"的宇宙法则。桌上摆放着四大元素的象征物------圣杯、宝剑、星币和权杖。他头上有无限符号,代表着无限的潜力。',
    color: '#E8B832',
    isFavorite: false
  },
  // ... 共 78 张牌
];

1.3 数据填充的工程化思考

为 78 张牌填写完整的中英文含义是一项大工程。在实际项目中,我有几点建议:

  1. 结构化优先:先确定接口字段,再填充数据
  2. 分批填充:按大阿卡纳(22 张)→ 权杖组(14 张)→ 圣杯组(14 张)→ 宝剑组(14 张)→ 星币组(14 张)分批完成
  3. 释义长度控制:正逆位释义各保持在 100-150 字之间,过长会影响阅读体验
  4. 颜色编码:为每张牌分配一个辨识色,增加视觉层次感

二、资源文件管理

2.1 字符串资源

json 复制代码
// resources/base/element/string.json
{
  "string": [
    { "name": "title_home", "value": "命运之轮" },
    { "name": "title_cards", "value": "全部牌义" },
    { "name": "title_spread", "value": "牌阵解读" },
    { "name": "title_favorites", "value": "我的收藏" },
    { "name": "today_card", "value": "今日塔罗" },
    { "name": "module_desc", "value": "塔罗牌占卜应用" },
    { "name": "EntryAbility_desc", "value": "塔罗牌主入口" },
    { "name": "EntryAbility_label", "value": "命运之轮" }
  ]
}

2.2 浮点尺寸资源

json 复制代码
// resources/base/element/float.json
{
  "float": [
    { "name": "app_title_size", "value": "28fp" },
    { "name": "app_subtitle_size", "value": "22fp" },
    { "name": "app_body_size", "value": "16fp" },
    { "name": "app_small_size", "value": "14fp" },
    { "name": "app_caption_size", "value": "12fp" },
    { "name": "app_card_radius", "value": "16vp" },
    { "name": "app_button_radius", "value": "24vp" }
  ]
}

2.3 颜色资源

json 复制代码
// resources/base/element/color.json
{
  "color": [
    { "name": "start_window_background", "value": "#1A0A2E" },
    { "name": "primary_text", "value": "#FFFFFF" },
    { "name": "secondary_text", "value": "#B8A8D0" }
  ]
}

为什么使用 $r() 引用资源?

  • 支持多语言适配(国际化)
  • 支持多设备适配(不同 dpi 自动缩放)
  • 编译时校验,避免硬编码错误

三、构建配置解析

3.1 项目级 build-profile.json5

json5 复制代码
{
  "app": {
    "signingConfigs": [],
    "products": [
      {
        "name": "default",
        "signingConfig": "default",
        "targetSdkVersion": "6.1.1(24)",  // 目标 SDK
        "compatibleSdkVersion": "6.1.0(23)", // 兼容最低版本
        "runtimeOS": "HarmonyOS",
        "buildOption": {
          "strictMode": {
            "caseSensitiveCheck": true,       // 大小写检查
            "useNormalizedOHMUrl": true       // 归一化 OHM URL
          }
        }
      }
    ],
    "buildModeSet": [
      { "name": "debug" },
      { "name": "release" }
    ]
  }
}

API 版本策略解读

参数 含义
compatibleSdkVersion 23 最低支持 API 23 的设备
targetSdkVersion 24 最高使用 API 24 的特性
runtimeOS HarmonyOS 仅运行在 HarmonyOS 上

3.2 模块级 build-profile.json5

json5 复制代码
{
  "apiType": "stageMode",        // Stage 模型
  "buildOption": {
    "resOptions": {
      "copyCodeResource": {
        "enable": false          // 不复制代码资源
      }
    }
  },
  "buildOptionSet": [
    {
      "name": "release",
      "arkOptions": {
        "obfuscation": {         // 代码混淆配置
          "ruleOptions": {
            "enable": false,     // 可开启以减小包体
            "files": ["./obfuscation-rules.txt"]
          }
        }
      }
    }
  ]
}

3.3 AppScope/app.json5 --- 全局应用配置

json5 复制代码
{
  "app": {
    "bundleName": "com.example.myapplication",  // 包名,全局唯一
    "vendor": "example",
    "versionCode": 1000000,      // 内部版本号
    "versionName": "1.0.0",     // 对外版本名
    "icon": "$media:layered_image",
    "label": "$string:app_name" // 应用名称(仅在 AppScope 定义一次)
  }
}

注意app_name 只需在 AppScopestring.json 中定义一次,不可在 entry 模块中重复定义,否则编译会报冲突。

3.4 module.json5 --- 模块配置

json5 复制代码
{
  "module": {
    "name": "entry",
    "type": "entry",              // entry 类型(可独立运行)
    "mainElement": "EntryAbility",
    "deviceTypes": ["phone"],
    "deliveryWithInstall": true,
    "installationFree": false,
    "pages": "$profile:main_pages", // 路由配置引用
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntry": "./ets/entryability/EntryAbility.ets",
        "exported": true,
        "skills": [
          {
            "entities": ["entity.system.home"],
            "actions": ["ohos.want.action.home"]
          }
        ]
      }
    ]
  }
}

关键字段解释

  • type: "entry":可独立安装运行的应用入口模块
  • skills:声明 Ability 的能力,entity.system.home + ohos.want.action.home 表明这是桌面入口
  • pages:引用 main_pages.json 中注册的所有页面路由

四、ArkTS 严格模式规则详解

在开发过程中,严格模式(arkts-no-untyped-obj-literals 等)是误报最多的配置。以下是完整的规则及应对方案:

4.1 规则清单

规则 含义 触发场景
arkts-no-untyped-obj-literals 对象字面量必须显式类型 const obj = {a:1, b:2}
arkts-no-noninferrable-arr-literals 数组字面量必须可推断类型 const arr = [{a:1, b:2}]
arkts-no-nullable-array 不允许可空数组类型 arr?: string[]
arkts-no-any 不允许 any 类型 let x: any
arkts-no-eval 不允许 eval eval(code)

4.2 实战解决方案

方案一:提取为具名变量

typescript 复制代码
// ❌ 错误:字面量数组无法推断
// ForEach(this.drawnCards, (item: NumberedCard) => { ... })

// ✅ 正确:显式声明后使用
const positions = ['过去', '现在', '未来'];

方案二:对象赋值使用类型断言

typescript 复制代码
// ❌ 可能的不严格写法
const card = { id: 0, name: '愚者' };

// ✅ 显式声明类型
const card: TarotCard = {
  id: 0, name: '愚者', englishName: '', arcana: '', // 全部字段
  number: '', keywords: '', meaningUp: '', meaningDown: '',
  description: '', color: '#FFF', isFavorite: false
};

方案三:@Component 属性必须初始化

typescript 复制代码
@Component
struct MyComponent {
  // ✅ 所有属性必须赋默认值
  name: string = '';
  count: number = 0;
  items: string[] = [];
  callback?: () => void;  // 可选属性用 ?
}

4.3 编译错误排查技巧

当 DevEco Studio 报编译错误时,按以下步骤排查:

  1. 看错误码arkts-no-xxx 的格式直接表明违反的规则
  2. 定位行号:大多数错误会精确到行
  3. 检查字面量:70% 的错误是对象/数组字面量缺少类型声明
  4. 检查类型:组件属性、函数参数是否都有明确类型

五、构建与发布流程

5.1 本地构建命令

bash 复制代码
# 使用 DevEco Studio 内置的 hvigor 构建
"D:\DevEco Studio\tools\node\node.exe" \
  "D:\DevEco Studio\tools\hvigor\bin\hvigorw.js" \
  --mode module \
  -p module=entry@default \
  -p product=default \
  -p requiredDeviceType=phone \
  assembleHap \
  --analyze=normal \
  --parallel \
  --incremental \
  --daemon

参数解读

参数 含义
--mode module 模块级构建
-p module=entry@default 构建 entry 模块的 default 产品
assembleHap 打包为 HAP 文件
--parallel 并行构建加速
--incremental 增量编译
--daemon 守护进程模式

5.2 构建产物

构建完成后,HAP 包位于:

复制代码
entry/build/default/outputs/default/entry-default-unsigned.hap

5.3 签名与发布

  1. 在 DevEco Studio 中配置签名证书(.p12 + .cer + .p7b
  2. 使用 buildBuild HAP(s) 生成已签名的 HAP
  3. 上传至华为应用市场 AppGallery Connect

六、项目经验总结

6.1 架构回顾

经过五篇文章的开发,我们的塔罗牌 App 形成了清晰的架构分层:

复制代码
┌─────────────────────────┐
│      UI 层 (pages/)      │ ← 5 个页面
├─────────────────────────┤
│    模型层 (model/)       │ ← TarotData 数据 + 管理器
├─────────────────────────┤
│     能力层 (ability/)    │ ← EntryAbility 入口
├─────────────────────────┤
│     配置层 (config/)     │ ← build / module / resources
└─────────────────────────┘

6.2 关键技术决策

决策 选择 理由
开发模型 Stage 模型 官方推荐,适合复杂应用
开发语言 ArkTS 类型安全,编译期校验
状态管理 静态类管理器 轻量、无第三方依赖
主题切换 订阅发布模式 解耦、可扩展
路由导入 @ohos.router API 23 兼容性

6.3 后续可扩展方向

  1. 数据持久化:Preferences 存储收藏数据
  2. 网络请求:对接塔罗牌 API 获取每日运势
  3. 动画效果:抽牌翻转动画、卡片入场动画
  4. 自定义牌阵:用户自由选择牌数和位置
  5. 多语言支持:英文版、日文版国际化
  6. Widget 服务卡片:桌面显示今日塔罗

结语

五篇文章,从环境搭建到发布上线,我们完整地走了一遍鸿蒙原生应用的开发流程。这个塔罗牌 App 虽小,但五脏俱全------路由导航、列表渲染、组件化、状态管理、主题切换、构建配置,覆盖了日常开发中的大多数场景。

鸿蒙生态正在快速发展,现在入局正当时。希望这个系列能成为你鸿蒙开发路上的实战参考。如果你有任何问题或想法,欢迎在评论区交流讨论!

项目代码 : 基于 HarmonyOS API 23 + Stage 模型 + ArkTS

源码位置 : entry/src/main/ets/

构建工具 : DevEco Studio + hvigor

全文完 🎉


附录:本系列目录

篇号 标题 核心内容
第一篇 环境搭建与首页开发 项目初始化、Stage 模型、Index 首页
第二篇 牌义列表与路由导航 CardListPage、CardDetailPage、路由传参
第三篇 牌阵解读与交互设计 SpreadPage、随机算法、正逆位判定
第四篇 收藏功能与主题切换 FavoriteManager、ThemeManager、FavPage
第五篇 数据模型与工程优化 TarotData、构建配置、严格模式、发布流程
相关推荐
程序猿追34 分钟前
那个右下角的小数字怎么“卡”住我打字——我用 HarmonyOS 自己写了一个字数限制输入框
pytorch·华为·harmonyos
古德new37 分钟前
鸿蒙PC使用electron迁移:Joplin Electron 桌面适配全记录
华为·electron·harmonyos
世人万千丶1 小时前
桌面便签小应用 - HarmonyOS ArkUI 开发实战-TextArea与Flex布局-PC版本
华为·harmonyos·鸿蒙·鸿蒙系统
慧海灵舟1 小时前
AGenUI 鸿蒙端实战踩坑录:从 Column 布局消失到异步组件宽度为 0
华为·harmonyos
yuegu7771 小时前
HarmonyOS应用<节气通>开发第33篇:状态管理实战
华为·harmonyos
YM52e2 小时前
买菜计算器小应用 - HarmonyOS ArkUI 开发实战-PC版本
学习·华为·harmonyos·鸿蒙·鸿蒙系统
阿捏利2 小时前
系列总览-鸿蒙科普系列完全指南
华为·harmonyos
小雨下雨的雨2 小时前
HarmonyOS ArkUI训练营入门-组件掌握系列-Animation 动画效果实现-PC版本
学习·华为·harmonyos·鸿蒙
yuegu7772 小时前
HarmonyOS应用<节气通>开发第32篇:ArkTS语法快速入门——从TypeScript到声明式UI的完整指南
harmonyos
2601_962072554 小时前
李梦娇常识4600问|题库|打印版
sql·华为od·华为·c#·华为云·.net·harmonyos