鸿蒙原生应用实战(五):塔罗牌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 张),代表日常事务
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):

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 字符串资源

// 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 浮点尺寸资源

// 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 颜色资源

// 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

{
  "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

{
  "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 — 全局应用配置

{
  "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 — 模块配置

{
  "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 实战解决方案

方案一:提取为具名变量

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

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

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

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

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

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

@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 本地构建命令

# 使用 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、构建配置、严格模式、发布流程
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐