笔的进化路线:鸿蒙 ArkTS API 24 笔记录入与数据检索实战




本文记录了在 HarmonyOS Next 平台上,使用 ArkTS API 24 开发「笔的进化路线」时间线应用的完整实践过程,涵盖数据模型设计、UI 布局方案、API 24 兼容性要点以及编译调试全流程。
一、项目背景与概述
1.1 为什么做这个应用?
书写工具——「笔」——是人类文明最重要的发明之一。从古埃及人用芦苇笔在纸莎草上书写,到今天我们用智能笔在平板上绘画和笔记,笔的进化历程本身就是一部浓缩的人类文明史。
然而,对于大多数学习者来说,这段历史散落在不同的资料中,缺乏一个直观、可交互的时间线展示工具。因此,我决定在 HarmonyOS Next 平台上,使用 ArkTS 语言(API 24)开发一个「笔的进化路线」应用,以时间线卡片的形式,清晰展示从古至今各类笔的发展脉络。
1.2 技术选型
| 技术项 | 选择 |
|---|---|
| 操作系统 | HarmonyOS Next (API 24) |
| 开发语言 | ArkTS (基于 TypeScript,兼容 API 24 版本) |
| 开发工具 | DevEco Studio |
| 数据存储 | 内存级 JSON 对象(模拟数据) |
| UI 布局 | 全 ArkTS 声明式组件 |
1.3 应用功能一览
- 时间线展示:以纵向时间轴布局展示 10 个笔的进化阶段
- 交互式卡片:点击卡片展开/收起详情信息
- 数据统计:底部统计面板展示进化阶段数量和跨度
- 视觉风格:卡片式圆形时间点 + 连接线,配合 Emoji 图标区分各笔型
二、应用设计与数据模型
2.1 数据接口定义
在 ArkTS API 24 中,interface 用于定义数据模型的结构化类型。我们定义了 PenStage 接口来描述每一个进化阶段:
interface PenStage {
id: number // 排序 ID
name: string // 笔的名称
icon: string // 图标 key
year: string // 出现的年代
origin: string // 起源地
desc: string // 详细介绍
details: string // 展开后的详细参数
}
接口设计要点:
- 所有字段均为必填(非可选),确保数据完整性
icon字段使用字符串 key,而不是直接存储 Emoji,便于后续扩展为图标字体或图片资源year和origin分离为独立字段,便于后续增加"按年代排序"或"按地域筛选"功能
2.2 模拟数据集
应用内置了 10 个进化阶段的数据,全部以 JSON 风格的对象数组在内存中构建。这里在 ArkTS 中使用了 push() 方法依次添加,而不是使用 map() 或 forEach(),原因将在第三章详细说明。
数据亮点:
| 序号 | 笔的名称 | 年代 | 发源地 | 关键成就 |
|---|---|---|---|---|
| 1 | 芦苇笔 | 约公元前3000年 | 古埃及·美索不达米亚 | 人类最早的书写工具 |
| 2 | 羽毛笔 | 公元6世纪 | 欧洲·中世纪 | 弹性笔尖,统治千年 |
| 3 | 金属笔尖笔 | 1803年 | 英国·伯明翰 | 首次金属笔尖量产 |
| 4 | 自来水笔(钢笔) | 1884年 | 美国·纽约 | 毛细供墨革命 |
| 5 | 圆珠笔 | 1943年 | 阿根廷·布宜诺斯艾利斯 | 滚珠带墨,全球普及 |
| 6 | 记号笔 | 1952年 | 美国·芝加哥 | 多表面书写 |
| 7 | 滚珠笔 | 1963年 | 日本·东京 | 水性+滚珠结合 |
| 8 | 中性笔(啫喱笔) | 1984年 | 日本·东京 | 凝胶墨水,学生首选 |
| 9 | 触控笔 | 2007年 | 全球·智能手机时代 | 数字输入复兴 |
| 10 | 智能笔 | 2010年代 | 全球·数字时代 | 书写+录音+云同步 |
2.3 数据初始化函数
在 aboutToAppear() 生命周期中调用 initData() 初始化数据。这是 ArkTS 中标准的启动逻辑:
aboutToAppear(): void {
this.initData()
}
initData() 内部创建了一个局部数组 data,逐条 push() 添加数据后,一次性赋值给 @State penList:
initData(): void {
let data: PenStage[] = []
data.push(this.makeStage(...))
data.push(this.makeStage(...))
// ...
this.penList = data
}
这里使用了一个辅助方法 makeStage() 来降低代码重复,提高可读性:
makeStage(id: number, name: string, icon: string, year: string,
origin: string, desc: string, details: string): PenStage {
let item: PenStage = {
id, name, icon, year, origin, desc, details
}
return item
}
三、ArkTS API 24 兼容性要点 —— 最重要的章节
在开发过程中,遇到了多个 API 版本差异问题。以下是 ArkTS API 24 的兼容性要点总结,这是本博客最核心的部分。
3.1 为什么 API 24 有特殊的限制?
HarmonyOS Next 的 ArkTS 是 TypeScript 的一个强约束子集。API 24 版本进一步收紧了语法支持范围,目的是:
- 运行时性能优化:静态分析可以生成更高效的字节码
- 内存安全:禁止某些动态特性,降低 OOM 风险
- 跨平台一致性:减少运行时行为差异
3.2 禁止使用的高级数组方法
API 24 不支持以下数组方法(部分列表):
| 方法 | 原因 | 替代方案 |
|---|---|---|
.map() |
匿名函数 → 函数对象创建开销大 | 使用 for 循环 + push() |
.forEach() |
同上 | 使用 for 循环 |
.filter() |
同上 | 使用 for 循环 + if |
.reduce() |
同上 | 使用 for 循环累加 |
.find() |
同上 | 使用 for 循环 + break |
.some() / .every() |
同上 | 使用 for 循环 + 布尔标记 |
.includes() |
运行时类型检查开销 | 使用 indexOf() !== -1 |
.startsWith() / .endsWith() |
字符串方法,部分版本不支持 | 使用 indexOf() === 0 或正则 |
实战方案:在本应用中,初始化 10 条数据使用了 push() 而非数组字面量一次性赋值。push() 是 API 24 支持的,但 forEach() 遍历渲染时需要使用 ArkTS 提供的 ForEach 组件而非数组方法。
3.3 @Builder 的限制
ArkTS 的 @Builder 装饰器是构建 UI 片段的核心机制,但有两个关键限制:
限制一:@Builder 不能有泛型参数
错误写法:
@Builder
MyCard<T>(item: T) { ... } // ❌ 编译错误
正确写法:必须显式指定具体类型
@Builder
StageCard(item: PenStage, isSelected: boolean) { ... } // ✅
限制二:@Builder 内部不能使用可选链
错误写法:
@Builder
SomeBuilder() {
Text(item?.name) // ❌ 编译错误
}
正确写法:使用三元运算符或短路逻辑
@Builder
SomeBuilder() {
Text(item ? item.name : '') // ✅
}
3.4 关于 .italic() 方法
这是实际遇到的编译错误。ArkTS API 24 的 TextAttribute 类型没有 .italic() 方法,必须使用 .fontStyle(FontStyle.Italic) 替代:
// ❌ 错误
Text('引言文字').italic(true)
// ✅ 正确
Text('引言文字').fontStyle(FontStyle.Italic)
3.5 立即执行函数表达式(IIFE)
在 @State 初始化中,不能使用 IIFE:
// ❌ 错误
@State data: SomeType[] = (() => {
// ...
})()
// ✅ 正确:在 aboutToAppear 中初始化
@State data: SomeType[] = []
aboutToAppear(): void {
this.initData()
}
3.6 Record 类型
API 24 的 Record<K, V> 类型支持有限。某些场景下建议使用自定义 interface 替代:
// 可能有问题
@State map: Record<string, number> = {}
// 更稳妥的方案
interface StringNumberMap {
[key: string]: number
}
@State map: StringNumberMap = {}
3.7 条件渲染语法
API 24 支持 if / else / else if 语句块用于条件渲染,但不支持 && 短路渲染:
// ✅ 正确
if (isSelected) {
Column() { ... }
}
// 不确定的写法
// { isSelected && <Column>...</Column> } // 兼容性存疑
3.8 循环渲染语法
使用 ForEach 组件代替 forEach() 方法:
// ✅ 正确
ForEach(this.penList, (item: PenStage) => {
this.StageCard(item, ...)
}, (item: PenStage) => {
return 'pen_' + item.id
})
// ❌ 错误:this.penList.forEach(...) 不兼容
ForEach 有三个参数:
- 数据源:数组
- 子组件生成函数:
(item, index?)→ 返回组件 - 键值生成函数:用于列表 diff 优化,可选但强烈推荐
四、UI 布局逐层解析
4.1 整体布局结构
Column (根容器)
├── TitleBar (@Builder)
├── Scroll
│ └── Column
│ ├── 引言文字
│ ├── ForEach 渲染 10 个 StageCard
│ ├── SummarySection (@Builder)
│ └── PageFooter (@Builder)
└── Scroll (占满剩余空间)
4.2 标题栏(TitleBar)
@Builder
TitleBar() {
Row() {
Text('🖊️').fontSize(28)
Text(' 笔的进化路线')
.fontSize(22)
.fontWeight(FontWeight.Bold)
.fontColor('#2C3E50')
}
.width('100%')
.padding(16)
.backgroundColor('#F8F9FA')
}
设计思路:
- 使用 Emoji
🖊️作为视觉锚点,无需额外图标库 - 固定灰色背景
#F8F9FA,与主内容区#F5F7FA微差区分 - 深色字体
#2C3E50保证可读性
4.3 时间轴卡片(StageCard)
这是应用的核心 UI 组件。每个卡片包含:
左侧时间轴:
- 圆形节点(
Circle组件),未选中时蓝色,选中时红色 - 节点下方垂直连线(2px 宽,灰色
#BDC3C7) - 选中时节点放大阴影(
shadow+radius: 6)
右侧内容卡片:
- 第一行:Emoji 图标 + 笔名 + 年代 + 展开/收起箭头
- 第二行:发源地
- 第三行:详细介绍文字
- 条件渲染:选中时显示详情区域(带分割线和
details内容)
交互逻辑:
.onClick(() => {
if (this.selectedIndex === item.id) {
this.selectedIndex = -1 // 点击已选中的卡片 → 收起
} else {
this.selectedIndex = item.id // 点击其他卡片 → 切换
}
})
4.4 统计面板(SummarySection)
@Builder
SummarySection() {
Column() {
Divider()
Text('📊 进化历程概览')
Row() {
this.StatChip('📅 跨越', '10 个阶段')
this.StatChip('🌍 从埃及', '到智能时代')
this.StatChip('✍️ 不变', '书写初心')
}
Text('从芦苇到智能,笔的进化也是人类文明的缩影。')
}
}
三个 StatChip 均匀分布在行内,使用了 FlexAlign.SpaceEvenly 实现平均间距。
4.5 空状态处理
当数据尚未加载完成时,显示空状态占位:
@Builder
EmptyStage(msg: string) {
Column() {
Text('📝').fontSize(64).opacity(0.3)
Text(msg).fontSize(16).fontColor('#999').margin({ top: 12 })
}
.width('100%')
.height('60%')
.justifyContent(FlexAlign.Center)
}
通过 this.penList.length === 0 条件控制,在实际设备上(数据即时初始化)几乎不可见,但作为健壮性设计保留。
五、Emoji 图标映射方案
5.1 为什么用字符串 key 而非直接写 Emoji?
在 ArkTS 组件中可以直接使用 Emoji(如 Text('🖊️')),但为了代码维护性,在数据层使用字符串 key,在展示层通过 iconToEmoji() 方法映射:
iconToEmoji(icon: string): string {
if (icon === 'reed') return '🌾'
if (icon === 'quill') return '🪶'
if (icon === 'metal_nib') return '🔩'
if (icon === 'fountain') return '🖋️'
if (icon === 'ballpoint') return '🖊️'
if (icon === 'marker') return '🖍️'
if (icon === 'rollerball') return '✒️'
if (icon === 'gel') return '💧'
if (icon === 'stylus') return '✏️'
if (icon === 'smart_pen') return '🧠'
return '📝'
}
优点:
- 数据层与展示层解耦——如需替换为自定义图标字体或 SVG,只需修改映射函数
- 集中管理,避免 Emoji 在不同编辑器中的渲染差异
- 便于单元测试——可以测试 key 覆盖率而无需处理 Unicode 渲染
5.2 Emoji 选择原则
每个 Emoji 的选择尽量贴近实物特征:
| 笔的种类 | Emoji | 选择理由 |
|---|---|---|
| 芦苇笔 | 🌾 稻禾 | 芦苇秆的视觉关联 |
| 羽毛笔 | 🪶 羽毛 | 直指材料来源 |
| 金属笔尖 | 🔩 螺母 | 金属机械感 |
| 钢笔 | 🖋️ 钢笔 | 标准书写符号 |
| 圆珠笔 | 🖊️ 圆珠笔 | 标准书写符号 |
| 记号笔 | 🖍️ 蜡笔 | 粗笔尖的联想 |
| 滚珠笔 | ✒️ 笔尖 | 强调书写端 |
| 中性笔 | 💧 水滴 | 啫喱墨水的水润感 |
| 触控笔 | ✏️ 铅笔 | 数字绘画工具的联想 |
| 智能笔 | 🧠 大脑 | 智能/科技属性 |
六、色彩设计方案
6.1 颜色体系
| 用途 | 色值 | 场景 |
|---|---|---|
| 页面背景 | #F5F7FA |
主背景,柔和灰蓝 |
| 标题栏背景 | #F8F9FA |
微差区分 |
| 卡片背景 | #FFFFFF |
纯白卡片,干净 |
| 主标题色 | #2C3E50 |
深蓝灰,专注 |
| 辅助文字 | #7F8C8D |
灰蓝,次要信息 |
| 时间轴连线 | #BDC3C7 |
浅灰,不抢眼 |
| 时间轴节点(默认) | #3498DB |
蓝色,中性 |
| 时间轴节点(选中) | #E74C3C |
红色,强调 |
| 分割线 | #ECF0F1 |
极浅灰,弱分割 |
| 统计卡片背景 | #F0F3F5 |
浅色,从属感 |
6.2 为什么选择这种色彩方案?
- 低饱和度基调:以灰、白、蓝为主色调,避免视觉疲劳
- 红色高亮:仅在选中状态使用红色(
#E74C3C),形成强烈的注意力锚点 - 自然渐变:从页面背景到卡片背景再到统计卡片,三级递进增加层次感
6.3 阴影设计
// 未选中
.shadow({ radius: 3, color: '#00000018' })
// 选中
.shadow({ radius: 8, color: '#00000018' })
选中时阴影半径从 3 扩大到 8,产生卡片"浮起"的视觉反馈。颜色使用低透明度黑色(18%),保证在浅色背景下自然柔和。
七、声明式 UI 与数据流
7.1 @State 数据驱动
应用中的两个核心状态:
@State penList: PenStage[] = [] // 数据源
@State selectedIndex: number = -1 // 当前选中项,-1 表示无选中
penList在aboutToAppear()中被填充,驱动ForEach渲染selectedIndex在点击卡片时更新,驱动isSelected条件判断
7.2 数据流向图
用户点击卡片
↓
.onClick() 更新 selectedIndex
↓
StageCard 的 isSelected 参数重新计算
↓
条件渲染:显示/隐藏 details 内容
↓
UI 自动刷新(无手动 DOM 操作)
整个过程完全符合声明式 UI 的单向数据流范式。
7.3 生命周期钩子
本应用使用了一个生命周期钩子:
aboutToAppear(): void {
this.initData()
}
aboutToAppear 在组件被创建且即将显示时调用,是 ArkTS 中初始化数据的最佳位置。对应的还有 aboutToDisappear(),在本应用中未使用,但可用于清理资源(如定时器、监听器)以防止内存泄漏。
八、编译与调试经验总结
8.1 常见编译错误及解决方案
| 错误代码 | 错误信息 | 原因 | 解决方案 |
|---|---|---|---|
| 10505001 | Property ‘italic’ does not exist | API 24 无 .italic() |
改为 .fontStyle(FontStyle.Italic) |
| 10505001 | Property ‘map’ does not exist | API 24 禁用 .map() |
用 for + push() 替代 |
| 10505001 | IIFE not supported | 初始化中使用 IIFE | 移到 aboutToAppear() |
| 10505001 | Generic @Builder | @Builder 不支持泛型 | 使用具体类型 |
| 10505001 | Optional chaining not supported | ?. 语法 |
使用三元运算符 |
8.2 构建命令
# 完整构建
hvigorw --mode module -p module=entry -p product=default assembleHap
# 清理后构建(解决缓存问题)
hvigorw clean --mode module -p module=entry -p product=default assembleHap
8.3 IDE 缓存问题
在开发过程中,有一个常见陷阱:命令行构建成功,但 IDE(DevEco Studio)仍然显示红标。
原因:IDE 的语法检查器与命令行编译器可能使用不同的缓存。
解决步骤:
Build → Clean Project(清理构建缓存)Build → Rebuild Project(重新构建,刷新 IDE 索引)- 若仍有红标:
File → Invalidate Caches → Invalidate and Restart - 重启后 IDE 重新索引整个项目
8.4 Build JS 阶段的作用
构建输出中经常看到:
UP-TO-DATE :entry:default@BuildJS...
这个阶段负责编译 ArkTS 代码中的 JavaScript 桥接层。如果你的应用包含网络请求、文件访问等依赖 JS 引擎的功能,这个阶段会输出对应的 JS 包。在本应用中(纯 ArkTS 声明式 UI),这个阶段基本是空的。
九、代码优化与最佳实践
9.1 组件拆分策略
本应用将 @Builder 拆分为 5 个独立片段:
| @Builder | 职责 | 复用性 |
|---|---|---|
TitleBar |
顶部标题 | 单次使用,但隔离使主代码更清晰 |
EmptyStage |
空状态占位 | 条件渲染,优雅降级 |
StageCard |
核心时间线卡片 | 循环渲染 10 次 |
SummarySection |
底部统计 | 单次使用,内含 3 个 StatChip |
StatChip |
单个统计指标 | 被 SummarySection 复用 3 次 |
PageFooter |
底部版权 | 单次使用 |
9.2 键值优化
在 ForEach 中,第三个参数是键值生成函数:
ForEach(this.penList, ..., (item: PenStage) => {
return 'pen_' + item.id
})
为什么需要键值?
- 当数据源发生变化(增/删/移动),框架需要识别哪些项是新增/删除/变更的
- 使用稳定的键值(如
'pen_' + id)可以最小化 DOM 更新范围
键值选择原则:
- 使用唯一且稳定的值,不要使用索引
- 键值前缀可以防止与其他列表的键值冲突
9.3 避免在 @Builder 中定义状态
每个 @Builder 不应该定义自己的 @State——它们是纯展示组件,所有的动态数据应该通过参数传入。这样确保了数据流的一致性,避免了状态分散在 UI 代码中难以追踪。
9.4 硬编码 vs 配置化
当前应用的数据直接写在代码中。如果需要支持多语言或从外部加载数据,可以:
- 抽离为 JSON 资源文件:放在
resources/rawfile/目录下 - 使用 Resource 类型:ArkTS 支持
$r()引用资源 - 网络加载:通过
http模块请求远程数据
// 未来扩展方向
import http from '@ohos.net.http'
fetchRemoteData(): void {
let req = http.createHttp()
req.request('https://api.example.com/pen-evolution', ...
(err, data) => {
if (data && data.responseCode === 200) {
this.penList = JSON.parse(data.result as string)
}
})
}
十、与「图书阅读记录 APP」的对比
在同一项目中,我们还开发了「图书阅读记录 APP」,这里做一些对比分析:
| 维度 | 图书阅读记录 APP | 笔的进化路线 APP |
|---|---|---|
| 数据类型 | 表单录入 + 列表 | 时间线展示 |
| 数据规模 | 动态增长(用户录入) | 固定 10 条(内置) |
| 交互复杂度 | 输入框、搜索、删除 | 点击展开/收起 |
| 布局复杂度 | 搜索栏 + 列表 | 时间轴 + 卡片 |
| 主要难点 | @State 的 IIFE 问题 | @Builder 泛型限制 |
| 共用技术点 | ForEach、@State、Scroll | ForEach、@State、Scroll |
| 共用兼容性点 | .map() 替代、.italic() 替代 | .map() 替代、.italic() 替代 |
两个应用共同展示了 ArkTS API 24 开发的全流程,覆盖了大多数常见场景。
十一、完整源码解读
以下是对 PenEvolution.ets 全文件的逐段解读。
11.1 入口与组件定义
@Entry
@Component
struct PenEvolution {
@Entry:标记该组件是页面的入口@Component:声明为一个可复用的 ArkTS 组件struct:ArkTS 使用结构体而非 class 定义组件
11.2 状态变量
@State penList: PenStage[] = []
@State selectedIndex: number = -1
@State装饰的变量变化时会触发 UI 重新渲染penList存储所有笔的进化数据selectedIndex存储当前选中卡片的 id,-1表示无选中
11.3 生命周期
aboutToAppear(): void {
this.initData()
}
这是组件即将显示前的回调,执行时机在 build() 渲染之前。
11.4 数据初始化
initData(): void {
let data: PenStage[] = []
data.push(this.makeStage(0, '芦苇笔', 'reed', ...))
data.push(this.makeStage(1, '羽毛笔', 'quill', ...))
// ... 共 10 条
this.penList = data
}
使用 let 创建局部变量,push 逐条添加,最后一次性赋值给 @State 变量。
11.5 辅助工厂方法
makeStage(id, name, icon, year, origin, desc, details): PenStage {
let item: PenStage = { id, name, icon, year, origin, desc, details }
return item
}
ES6 简洁属性语法({ id } 等同 { id: id })在 ArkTS 中可用。
11.6 Emoji 映射
iconToEmoji(icon: string): string {
if (icon === 'reed') return '🌾'
// ... 共 10 个分支
}
使用全 if 链而非 switch,因为 ArkTS API 24 对 switch 的一些模式支持不确定。
11.7 主 build() 函数
build() {
Column() {
this.TitleBar()
if (this.penList.length === 0) {
this.EmptyStage('加载笔的进化数据...')
} else {
Scroll() {
Column() {
Text('一支笔的进化史,就是半部人类文明史。')
.fontStyle(FontStyle.Italic)
ForEach(this.penList, (item) => {
this.StageCard(item, this.selectedIndex === item.id)
}, (item) => 'pen_' + item.id)
this.SummarySection()
this.PageFooter()
}
}
}
}
}
结构亮点:
- 空状态保护在
Scroll外层,避免空数组渲染空 Scroll ForEach使用键值'pen_' + id确保 diff 效率selectedIndex === item.id作为布尔表达式传入,简洁直观
十二、ArkTS API 24 开发者备忘录
12.1 ✅ 推荐的写法
// 循环:for + push
let arr: string[] = []
for (let i = 0; i < 10; i++) {
arr.push('item' + i)
}
// 条件渲染:if 语句
if (condition) {
ComponentA()
} else {
ComponentB()
}
// 字体样式
Text().fontStyle(FontStyle.Italic)
// ForEach
ForEach(data, (item) => { Component(item) }, (item) => key)
// @Builder
@Builder
MyBuilder(param: SpecificType) { ... }
12.2 ❌ 避免的写法
// 数组方法
data.map(x => ...)
data.forEach(x => ...)
data.filter(x => ...)
data.find(x => ...)
data.some(x => ...)
data.includes(x)
// 可选链
obj?.prop
arr?.[0]
// IIFE
@State x = (() => { ... })()
// 泛型 @Builder
@Builder
MyBuilder<T>(param: T) { ... }
// 不支持的样式方法
.italic(true)
12.3 调试技巧
- 打印日志:使用
console.info()、console.warn()、console.error() - 断点调试:DevEco Studio 支持在 ArkTS 代码中设置断点
- 布局边界:暂时为组件设置
.border(1, Color.Red)查看布局边界 - 隔离测试:将复杂组件的最小复现提取到独立文件测试
十三、未来扩展方向
13.1 功能扩展
- 数据持久化:使用
@ohos.data.preferences或@ohos.data.distributedKVStore存储用户笔记 - 图片展示:为每种笔增加历史照片或示意图
- 动画效果:卡片进入时使用
animateTo()添加过渡动画 - 搜索过滤:按年代范围或发源地筛选
- 多语言:支持英文等其他语言
13.2 性能优化
- LazyForEach:数据量增大时(>50 条),使用
LazyForEach替代ForEach实现虚拟列表 - @Prop/@Link:当数据层级更深时,使用
@Prop或@Link传递状态,减少不必要的渲染 - 状态合并:多个
@State合并为一个对象,减少变更通知次数
13.3 架构演进
当前:单体 struct + @Builder
↓
中期:拆分为独立 Component(PenCard、TimeLine、StatPanel)
↓
远期:MVVM + 数据层(Repository + DataSource)
十四、总结
「笔的进化路线」应用虽然功能简洁,但覆盖了 ArkTS API 24 开发中的核心知识点:
- 数据模型设计:使用
interface定义结构化类型 - 声明式 UI:
@State+ 条件渲染 +ForEach循环渲染 - @Builder 组件化:将 UI 片段封装为可复用的构建器
- API 24 兼容性:绕过
map、forEach、italic、IIFE 等限制 - 交互反馈:点击选中/取消选中、阴影变化、时间轴圆点颜色变化
- 编译调试:命令行构建、IDE 缓存清理、错误代码定位
更重要的是,这个应用展示了如何在 API 24 的限制下,依然写出清晰、可维护的代码——不使用高级数组方法不等于不能写出好代码,用 for + push() 同样优雅,用 if 链替代 map 同样可读。
笔的进化还在继续——从芦苇到智能笔,从模拟到数字。而我们的 HarmonyOS 开发之旅,也才刚刚开始。
更多推荐



所有评论(0)