在这里插入图片描述

在这里插入图片描述
——以《孔雀东南飞》App为例

一、引言

在移动应用开发领域,技术选型与用户体验之间的平衡始终是开发者面临的核心课题。随着HarmonyOS生态的蓬勃发展,越来越多的开发者开始关注这一新兴平台的应用开发范式。本文将以一个实际落地的项目——《孔雀东南飞》古诗文阅读应用——为例,系统地阐述基于HarmonyOS 6.1.1(API 24)平台、采用Stage模型和ArkTS语言开发原生应用的全流程技术实践。

本文的目标读者为具备一定前端开发基础、希望快速上手HarmonyOS原生应用开发的工程师。文章将从项目架构、开发环境搭建、UI组件实践、状态管理、资源管理、构建配置、性能优化等维度展开,力求呈现一个完整的、可复用的技术参考。

二、项目背景与技术选型

2.1 项目概述

《孔雀东南飞》阅读应用是一个面向古诗文爱好者的轻量级阅读工具。应用的核心功能包括:

  • 全文展示:完整呈现《孔雀东南飞》全诗357行正文及序言
  • 字体调节:支持动态调整正文字号,适应不同阅读场景
  • 滚动浏览:提供流畅的长文滚动体验
  • 信息展示:标题、序言、行数统计等辅助信息

从技术角度看,这个应用麻雀虽小五脏俱全,覆盖了HarmonyOS应用开发的核心技术点:页面布局、组件交互、状态管理、资源引用、构建配置等。

2.2 技术栈选型

技术维度 选型方案 说明
开发语言 ArkTS HarmonyOS原生声明式UI语言
编程模型 Stage模型 HarmonyOS推荐的应用模型
UI框架 ArkUI 声明式UI开发框架
构建工具 hvigor HarmonyOS专用构建工具
目标平台 HarmonyOS 6.1.1 API 24
IDE DevEco Studio 官方集成开发环境

2.3 为什么选择Stage模型

HarmonyOS提供了两种应用模型:FA(Feature Ability)模型和Stage模型。Stage模型从API 9开始引入,并在后续版本中逐步完善。本项目选择Stage模型,主要基于以下考量:

  1. 组件化架构:Stage模型以Ability为基本单位,每个Ability可以独立开发、测试和部署
  2. 生命周期管理:Stage模型提供了更清晰的生命周期回调,便于资源管理
  3. 上下文隔离:不同Ability拥有独立的Context,避免了全局状态污染
  4. 未来兼容性:Stage模型是HarmonyOS未来的发展方向,FA模型已逐步进入维护模式

三、开发环境配置

3.1 环境需求

开发本应用所需的基础环境如下:

  • 操作系统:Windows 10/11 或 macOS
  • IDE:DevEco Studio 5.0+
  • SDK:HarmonyOS SDK 6.1.1(API 24)
  • Node.js:v18.x(由DevEco Studio内置管理)
  • Ohpm:HarmonyOS包管理器

3.2 项目创建与SDK配置

创建项目时,关键配置项如下:

build-profile.json5中,核心配置为:

{
  "app": {
    "products": [
      {
        "name": "default",
        "targetSdkVersion": "6.1.1(24)",
        "compatibleSdkVersion": "6.1.1(24)",
        "runtimeOS": "HarmonyOS"
      }
    ]
  }
}

这里的targetSdkVersioncompatibleSdkVersion均设置为6.1.1(24),其中24即为API Level。这一配置决定了应用能够使用的系统API范围。设置为API 24意味着可以充分利用HarmonyOS 6.1.1提供的最新特性,同时保持与同一大版本内设备的兼容性。

entry/build-profile.json5中,需要指定API类型为Stage模式:

{
  "apiType": "stageMode",
  "buildOption": {
    "resOptions": {
      "copyCodeResource": {
        "enable": false
      }
    }
  }
}

3.3 模块配置解析

entry/src/main/module.json5是模块的核心配置文件,包含了Ability注册、页面路由等关键信息:

{
  "module": {
    "name": "entry",
    "type": "entry",
    "mainElement": "EntryAbility",
    "deviceTypes": ["phone"],
    "pages": "$profile:main_pages",
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntry": "./ets/entryability/EntryAbility.ets",
        "exported": true,
        "skills": [
          {
            "entities": ["entity.system.home"],
            "actions": ["ohos.want.action.home"]
          }
        ]
      }
    ]
  }
}

关键配置项解读:

  • mainElement:指定应用入口Ability
  • deviceTypes:声明支持的设备类型。当前仅配置phone,可按需扩展为tablet2in1
  • pages:引用$profile:main_pages,指向resources/base/profile/main_pages.json中的页面路由配置
  • skills:声明Ability能够响应的系统意图(Want),entity.system.home表示桌面图标入口

四、ArkTS语言特性实践

4.1 ArkTS与TypeScript的关系

ArkTS是HarmonyOS原生应用开发语言,它在TypeScript基础上进行了裁剪和增强:

  • 类型系统:继承TypeScript的静态类型系统,所有变量必须有类型声明或初始化推导
  • 装饰器:新增@Component@Entry@State等装饰器,用于声明式UI
  • 运行时:运行在ArkCompiler引擎上,而非JavaScriptCore或V8
  • 限制:不支持any类型(在严格模式下)、不支持动态属性添加

4.2 @Component与@Entry装饰器

在ArkUI中,每一个UI页面都是一个Component。@Component装饰器将一个类标记为UI组件,而@Entry则标记该组件为页面入口。

@Entry
@Component
struct Index {
  // 组件成员变量和build方法
}

这里有几个值得注意的要点:

  1. struct而非class:在ArkTS中,组件使用struct关键字定义,而非class。struct是值类型,具有更好的内存布局和性能表现
  2. 不可继承:struct组件不支持继承,所有组件都是独立的
  3. 必须实现build():每个组件必须实现build()方法,返回组件的UI描述

4.3 @State装饰器的响应式原理

@State是ArkUI最核心的装饰器之一,它标记的变量会触发UI的自动更新:

@State currentFontSize: number = 18;

currentFontSize的值发生变化时,所有依赖该变量的UI节点会自动重新渲染。其工作原理可以概括为:

  1. 依赖收集:build()方法执行时,框架自动记录哪些UI节点读取了@State变量
  2. 变更检测:当@State变量被赋值时,框架触发变更检测
  3. 最小化更新:框架仅重新渲染依赖该变量的UI节点,而非整个页面

这种"响应式编程"模型大大简化了UI开发——开发者不再需要手动操作DOM或调用setState(),只需修改数据,UI自动同步。

4.4 readonly修饰符的使用

本应用中将诗歌文本数据声明为readonly

private readonly poemTitle: string = '孔雀东南飞';
private readonly poemLines: string[] = [/* 357行诗句 */];

readonly是TypeScript/ArkTS的类型系统特性,它确保变量在初始化后不再被修改。对于静态数据(如诗歌文本),使用readonly有以下好处:

  • 编译期检查:任何对readonly变量的修改都会被编译器捕获并报错
  • 语义明确:清晰地表明这些数据是只读的,不会在运行时发生变化
  • 性能优化:编译器可以基于readonly做出更激进的优化决策

五、UI组件与布局设计

5.1 整体布局架构

应用的UI结构采用"Column容器 + Scroll滚动区域"的经典布局模式:

Column (根容器,100%宽高)
├── Column (标题区域)
│   ├── Text (标题:"孔雀东南飞")
│   ├── Text (副标题:"(并序)")
│   └── Text (序言正文)
├── Row (字体调节栏)
│   ├── Text ("A-")
│   ├── Slider (字号滑块)
│   └── Text ("A+")
├── Divider (分隔线)
├── Scroll (诗歌正文区域,layoutWeight占满剩余空间)
│   └── Column
│       └── ForEach(this.poemLines, ...)
│           ├── Blank (空行 = 段落间距)
│           └── Text (每行诗句)
└── Row (底部信息栏)
    └── Text ("共 XXX 行")

这种布局的特点是:

  • 垂直流式排列:Column容器是ArkUI中最基本的垂直布局容器
  • 弹性空间分配:通过layoutWeight(1)让Scroll区域填充剩余空间
  • 边界明确:每个区域职责清晰,便于维护和扩展

5.2 标题区域的Text组件使用

标题区域展示了三个层次的文本信息:

Text(this.poemTitle)
  .fontSize(28)
  .fontWeight(FontWeight.Bold)
  .fontColor('#2c1810')
  .letterSpacing(6)

ArkUI的Text组件支持丰富的样式属性:

  • fontSize:字体大小,接受number(fp单位)或Resource类型($r()引用)
  • fontWeight:字重,接受FontWeight枚举(Bold、Medium、Regular等)或number(100-900)
  • fontColor:字体颜色,支持十六进制、RGB、Resource引用
  • letterSpacing:字符间距,对中文文本的排版效果尤为重要
  • fontStyle:字体风格(Normal或Italic)
  • textAlign:文本对齐方式(Start、Center、End)

值得注意的是,Text组件中的文本默认不会自动换行——当文本超出容器宽度时,需要通过.maxLines().textOverflow()控制截断行为,或通过约束宽度触发自动换行。

5.3 Slider滑块组件的双向绑定

字体大小调节是本应用的核心交互功能,通过Slider组件实现:

Slider({
  value: this.currentFontSize,
  min: this.minFontSize,
  max: this.maxFontSize,
  step: 1,
  style: SliderStyle.OutSet
})
  .width(160)
  .blockColor('#8b7355')
  .trackColor('#d4c5a9')
  .selectedColor('#8b7355')
  .onChange((value: number) => {
    this.currentFontSize = value;
  })

Slider的参数配置要点:

  • value:当前值,绑定@State变量,实现双向联动
  • min/max:取值范围(14-28),与实际阅读字号范围对应
  • step:步长为1,确保字号平滑变化
  • style:OutSet样式,滑块在轨道外侧

.onChange()回调在用户滑动滑块时触发。这里有一个细节:@State currentFontSize的变化会导致所有引用该变量的Text组件重新渲染,包括Scroll区域中的每一行诗句。对于357行文本,ArkUI的diff算法足够高效,不会出现明显的性能问题。

5.4 Scroll滚动组件与长列表优化

Scroll是ArkUI中实现内容滚动的核心组件:

Scroll() {
  Column() {
    ForEach(this.poemLines, (line: string) => {
      if (line === '') {
        Blank().height(12)
      } else {
        Text(line)
          .fontSize(this.currentFontSize)
          .fontColor('#3c2e22')
          .lineHeight(this.currentFontSize + 12)
          .letterSpacing(1.5)
          .textAlign(TextAlign.Center)
      }
    }, (line: string) => line)
  }
  .width('100%')
  .padding({ left: 12, right: 12, top: 16, bottom: 40 })
}
.width('100%')
.layoutWeight(1)
.scrollBar(BarState.Off)

这里有几个关键技术点:

Scroll与layoutWeight的配合
Scroll的父容器是Column,Column中的其他子组件(标题、滑块、分隔线)都拥有固定高度。Scroll通过layoutWeight(1)获得Column分配的全部剩余空间,实现了"固定头部 + 可滚动内容"的经典布局模式。

ForEach的key生成

ForEach(this.poemLines, (line: string) => { ... }, (line: string) => line)

ForEach的第三个参数是keyGenerator函数,为每个列表项生成唯一标识。这里直接使用诗句文本作为key。但由于诗句文本可能有重复(虽然本例中没有),更健壮的做法是使用索引拼接:

ForEach(this.poemLines, (item, index) => { ... }, (item, index) => index.toString())

条件渲染的处理
在ForEach循环中,通过判断line === ''来决定渲染Blank(空行间距)还是Text(诗句)。这是ArkUI中条件渲染的常见模式——在循环中直接使用if/else分支。

.scrollBar(BarState.Off)
隐藏滚动条,提供更沉浸的阅读体验。但需要注意:完全隐藏滚动条可能会降低可发现性(用户可能不知道内容可以滚动)。替代方案是使用.scrollBar(BarState.Auto)(自动显示/隐藏)或提供视觉提示。

5.5 Blank与Divider的分隔效果

应用中有两处视觉分隔:

  1. 段落之间的Blank:在诗歌正文中,空行用Blank().height(12)实现。Blank是ArkUI的弹性空白组件,当其位于Column中且设置了固定高度时,表现为固定间距占位

  2. 标题与正文之间的Divider

Divider()
  .height(1)
  .color('#d4c5a9')
  .width('90%')

Divider是水平分割线组件,默认占满容器宽度,但可以通过width()约束。这里设置为90%宽度且居中,形成了视觉上的"留白"效果,更符合古典审美。

六、资源管理与引用

6.1 资源文件组织

HarmonyOS的资源管理遵循"限定词 + 资源类型"的目录结构:

resources/
├── base/           # 基础资源(默认)
│   ├── element/    # 基础元素(color, float, string等)
│   │   ├── color.json
│   │   ├── float.json
│   │   └── string.json
│   ├── media/      # 媒体资源(图片等)
│   └── profile/    # 配置文件
│       ├── backup_config.json
│       └── main_pages.json
└── dark/           # 深色模式限定资源
    └── element/
        └── color.json

这种资源目录结构的优势在于:

  • 多设备适配:通过添加限定词目录(如land横屏、dark深色模式)实现资源自动匹配
  • 资源复用:同一资源ID在不同限定词下可对应不同实际资源
  • 编译期优化:构建时自动筛选最匹配的资源,剔除无用资源

6.2 $r()资源引用的优势

在ArkUI中,资源引用使用$r()函数:

// 引用color资源
.fontColor($r('app.color.primary_text'))

// 引用float资源
.fontSize($r('app.float.page_text_font_size'))

相比直接在代码中硬编码数值或颜色,使用$r()有以下优势:

  1. 多主题支持:深色模式下自动切换资源值
  2. 国际化支持:不同语言区域自动加载对应字符串
  3. 编译期检查:引用的资源ID在编译时验证,避免运行时找不到资源
  4. 包体积优化:未使用的资源会被构建工具剔除

在本应用中,出于简化设计,部分颜色直接使用了十六进制字符串(如'#2c1810')。在实际生产项目中,建议将所有颜色值定义到color.json中,通过$r()引用,以便后续主题扩展。

6.3 float.json的配置技巧

float.json用于定义浮点数值资源,常用于字体大小、间距、圆角等:

{
  "float": [
    {
      "name": "page_text_font_size",
      "value": "50fp"
    }
  ]
}

ArkUI中的尺寸单位体系:

单位 说明 适用场景
fp 字体像素,跟随系统字体缩放 文本字号
vp 虚拟像素,1vp ≈ 1px(160dpi屏幕) 布局尺寸、间距
px 物理像素,不建议直接使用 极少用

本应用中的字号范围14-28即使用fp单位,跟随系统字体缩放设置,确保不同用户偏好下的可读性。

七、诗歌数据的组织策略

7.1 数据结构的选型

本应用将357行诗歌存储为string[]数组:

private readonly poemLines: string[] = [
  '孔雀东南飞,五里一徘徊。',
  '"十三能织素,十四学裁衣。',
  // ... 355 more lines
  '多谢后世人,戒之慎勿忘!'
];

选择数组而非其他数据结构的原因:

  • 有序访问:诗句按顺序排列,数组天然的索引访问符合需求
  • 简单直接:不需要键值对、关系型查询等复杂操作
  • 内存紧凑:数组在内存中是连续存储的,访问效率高

7.2 空行作为段落分隔

诗中通过空字符串('')标记段落分隔:

'府吏得闻之,堂上启阿母:',
'"儿已薄禄相,幸复得此妇。',
'结发同枕席,黄泉共为友。',
'共事二三年,始尔未为久。',
'女行无偏斜,何意致不厚?"',
'',   // ← 段落分隔
'阿母谓府吏:"何乃太区区!',

这种设计将"数据"和"展示逻辑"分离:数组只存储原始数据(包括空行标记),渲染逻辑负责将空行转换为UI间距。如果将来需要调整段落间距,只需修改Blank的高度,无需改动数据。

7.3 大数据量下的性能考量

对于357行文本,直接渲染所有Text组件不会造成性能问题。但如果诗歌行数扩展到数千行,则需要考虑以下优化方案:

方案一:LazyForEach懒加载

LazyForEach(this.dataSource, (item: string) => {
  Text(item).fontSize(18)
}, (item: string) => item)

LazyForEach只在项进入可视区域时才创建组件,对超长列表场景性能提升显著。

方案二:Scroll的NestedScroll嵌套

对于包含多个独立滚动区域的复杂页面,可以使用NestedScroll实现嵌套滚动,提供统一、流畅的滚动体验。

不过对于当前应用场景(357行),使用基本的ForEach已经足够。过早优化是万恶之源——在确实遇到性能瓶颈之前,保持代码的简洁和可读性更为重要。

八、构建与部署

8.1 hvigor构建流程

HarmonyOS项目的构建由hvigor工具驱动,构建流程分为以下阶段:

  1. PreBuild(预构建):检查环境、解析依赖
  2. MergeProfile(配置合并):合并module和app级别的配置
  3. ProcessResource(资源处理):编译资源文件,生成资源索引
  4. CompileArkTS(源码编译):将ArkTS编译为方舟字节码
  5. PackageHap(打包):将编译产物打包为HAP文件
  6. SignHap(签名):对HAP包进行数字签名
  7. CollectDebugSymbol(调试符号):收集调试信息

构建成功的输出日志:

> hvigor BUILD SUCCESSFUL in 10s 39ms

对于本应用,全量构建时间约10秒。得益于hvigor的增量编译机制,修改代码后的重新构建通常只需2-3秒。

8.2 签名配置

构建输出的日志中有一条WARN:

> hvigor WARN: Will skip sign 'hos_hap'. No signingConfigs profile is configured.

这意味着HAP包未签名。在调试阶段不影响设备部署(DevEco Studio会自动使用debug签名),但发布到应用市场需要配置正式签名。签名配置在build-profile.json5中:

{
  "app": {
    "signingConfigs": [
      {
        "name": "mySigning",
        "material": {
          "certPath": "./signing/myCert.cer",
          "keyPath": "./signing/myKey.key",
          "keyStorePath": "./signing/myKey.p12",
          "keyStorePassword": "******",
          "keyAlias": "myAlias",
          "keyPassword": "******"
        },
        "signAlg": "SHA256withECDSA",
        "profile": "./signing/myProfile.p7b"
      }
    ]
  }
}

8.3 调试与预览

DevEco Studio提供了多种调试方式:

  1. Previewer预览器:快速查看UI效果,无需真机
  2. Local Emulator本地模拟器:模拟HarmonyOS设备环境
  3. Remote Emulator远程模拟器:华为云提供的在线调试环境
  4. 真机调试:通过USB连接HarmonyOS设备直接运行

其中Previewer是最常用的开发调试方式,支持实时预览和交互操作。

九、性能优化与最佳实践

9.1 当前应用的性能分析

对本应用进行性能分析,主要关注以下指标:

指标 评估 优化空间
冷启动时间 <1s 无需优化
页面渲染 一次性渲染357个Text组件 中等
字体滑动响应 实时更新,无卡顿 良好
滚动流畅度 60fps 良好
内存占用 约20MB 良好

9.2 潜在优化方向

尽管当前应用性能表现良好,仍有以下优化方向值得探索:

文本组件复用
对于超长文本,可以合并相邻的Text组件,减少组件树深度。例如,将连续10行文本合并到一个Text中,使用\n换行:

// 优化前:10个Text组件
// 优化后:1个Text组件 + 换行符
Text(line1 + '\n' + line2 + '\n' + ...)

这种方式可以显著减少组件数量,但牺牲了每行的独立控制能力(如行级点击事件)。

预计算行高
在ForEach中使用.lineHeight(this.currentFontSize + 12)每次都会重新计算。可以在onChange中预计算并缓存:

@State lineHeight: number = 30;

// 在字体变更时
.onChange((value: number) => {
  this.currentFontSize = value;
  this.lineHeight = value + 12;
})

减少不必要的状态订阅
当前所有Text组件都订阅了currentFontSize的变化。如果后续添加更多可变样式(如字体颜色、行间距),可以考虑将样式属性分组,减少单个@State变量的影响范围。

9.3 内存管理要点

ArkCompiler的垃圾回收机制自动管理对象内存,但开发者仍需要注意以下事项:

  1. 避免闭包陷阱:在ForEach循环中创建闭包时,确保不持有外部大对象的引用
  2. 及时释放资源:在Ability的onDestroy回调中清理定时器、监听器等
  3. 谨慎使用全局变量:全局变量不会被GC回收,应尽量避免使用

十、扩展与演进方向

10.1 功能扩展路线图

本应用作为一个基础框架,可以在以下方向进行扩展:

第一阶段:内容丰富

  • 支持多首古诗的切换阅读
  • 添加诗词语音朗读功能
  • 集成注释和译文

第二阶段:交互增强

  • 添加夜间模式/护眼模式
  • 支持书签和阅读进度保存
  • 添加字体类型切换(楷体、宋体等)

第三阶段:社区化

  • 用户评论和笔记功能
  • 分享功能
  • 每日一诗推荐

10.2 多设备适配

HarmonyOS的一大优势是跨设备无缝体验。通过修改deviceTypes配置,可以快速扩展到其他设备:

"deviceTypes": ["phone", "tablet", "2in1", "wearable"]

不同设备的UI适配策略:

  • 平板:利用栅格布局(GridRow/GridCol)分栏显示
  • 折叠屏:监听折叠状态,在不同屏幕尺寸下切换布局
  • 车机:简化UI层级,适配驾驶场景交互

十一、总结

本文以《孔雀东南飞》阅读应用的开发为实践案例,系统地介绍了HarmonyOS 6.1.1(API 24)平台下使用Stage模型和ArkTS语言进行原生应用开发的全流程。

从项目实践的角度,本文覆盖了从环境配置、UI组件开发、状态管理、资源管理到构建部署的完整技术栈。从技术原理的角度,本文深入探讨了ArkUI响应式编程模型、组件生命周期、布局系统、性能优化等核心话题。

《孔雀东南飞》作为一个轻量级、目的明确的阅读工具,特别适合作为HarmonyOS入门开发的练手项目。它涵盖了Stage模型应用的基本骨架,又因为需求简洁而避免了复杂的业务逻辑干扰,让开发者可以专注于理解ArkTS和ArkUI的核心机制。

在HarmonyOS生态日益成熟的今天,掌握这套技术栈对于移动开发者而言具有长远的战略价值。无论是开发自有应用,还是为企业构建跨设备解决方案,本文涉及的Stage模型、ArkTS语言、ArkUI框架都将成为未来开发工作中的基础能力。

"纸上得来终觉浅,绝知此事要躬行。"技术的提升终究离不开持续的实践。希望本文能为正在学习HarmonyOS开发的同行者提供一份有价值的参考。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐