# 待办清单应用 — HarmonyOS状态管理与列表渲染实战

📅 2026/7/26 10:28:41
# 待办清单应用 — HarmonyOS状态管理与列表渲染实战
一、应用概述待办清单Todo List是移动应用开发中最经典的数据管理类应用案例。它涵盖了对数据的增Add、删Delete、改Toggle查View这一完整的CRUD操作闭环是理解和掌握任何前端框架数据处理能力的理想样本。本篇博文深入剖析一个基于HarmonyOS ArkUI/ArkTS开发的待办清单应用。该应用实现了待办事项的完整生命周期管理用户可以添加新的待办事项、点击事项前的复选框标记完成状态、通过滑动或点击删除按钮移除事项同时界面顶部实时显示待办事项的统计信息——总事项数、已完成数和未完成数。与传统的待办清单不同该应用在交互细节上做了精心打磨新增事项时带有淡入动画效果标记完成时事项文字以横线划除并变为灰色删除事项时带有缩小消失的动画。这些细腻的动画反馈使得应用在功能完整的基础上用户交互体验也得到了显著提升。从技术学习的角度来看待办清单应用涵盖了ArkTS的以下核心知识点State数组状态管理、ForEach列表渲染、条件渲染、组件间通信Prop和Link、以及动画系统的入门使用。这些知识点在HarmonyOS应用开发中具有极高的通用性和实用价值。二、技术架构2.1 组件层次结构待办清单应用采用主-子组件架构一个主组件Index负责整体布局和状态管理一个子组件TodoItem负责单个待办事项的展示和交互。┌─────────────────────────────────────┐ │ Entry Component │ │ struct Index │ │ ┌───────────────────────────────┐ │ │ │ 标题: 待办清单 │ │ │ ├───────────────────────────────┤ │ │ │ 统计栏: 总/已完成/未完成 │ │ │ ├───────────────────────────────┤ │ │ │ 输入框 添加按钮 │ │ │ ├───────────────────────────────┤ │ │ │ List 列表区域 │ │ │ │ ┌─────────────────────────┐ │ │ │ │ │ Component TodoItem │ │ │ │ │ │ [☐] 学习HarmonyOS │ │ │ │ │ ├─────────────────────────┤ │ │ │ │ │ Component TodoItem │ │ │ │ │ │ [☑] 写技术博客 │ │ │ │ │ └─────────────────────────┘ │ │ │ └───────────────────────────────┘ │ └─────────────────────────────────────┘2.2 数据模型// 待办事项数据模型 interface Todo { id: number // 唯一标识 text: string // 事项内容 completed: boolean // 完成状态 }数据模型是所有数据操作的基础。这里的Todo接口定义了一个待办事项的三个核心属性id用于唯一标识和高效更新/删除text存储用户输入的事项内容completed标记完成状态用于UI展示和统计计算。2.3 状态管理设计主组件Index维护以下核心状态State todos: Todo[] [] // 待办事项列表 State newTodoText: string // 新事项输入文本todos数组持有所有待办事项数据是应用的数据中枢。所有列表渲染、统计计算、增删改操作都围绕这个数组展开。newTodoText绑定输入框的文本内容用于控制添加按钮的启用状态和获取新事项的文本。三、核心代码分析3.1 主组件实现Entry Component struct Index { State todos: Todo[] [] State newTodoText: string build() { Column() { // 标题 Text(待办清单) .fontSize(28) .fontWeight(FontWeight.Bold) .margin({ top: 20, bottom: 10 }) // 统计信息 Row() { Text(总计: ${this.todos.length}) .fontSize(16) .fontColor(#666) Text(已完成: ${this.todos.filter(t t.completed).length}) .fontSize(16) .fontColor(Color.Green) .margin({ left: 15 }) Text(未完成: ${this.todos.filter(t !t.completed).length}) .fontSize(16) .fontColor(Color.Red) .margin({ left: 15 }) } .margin({ bottom: 15 }) // 输入区域 Row() { TextInput({ placeholder: 输入新的待办事项..., text: this.newTodoText }) .onChange((value: string) { this.newTodoText value }) .layoutWeight(1) .height(48) .borderRadius(8) .backgroundColor(#FFF) Button(添加) .margin({ left: 10 }) .onClick(() { this.addTodo() }) .enabled(this.newTodoText.trim().length 0) } .width(90%) .margin({ bottom: 20 }) // 待办列表 List() { ForEach(this.todos, (item: Todo) { ListItem() { TodoItem({ todo: item, onToggle: () this.toggleTodo(item.id), onDelete: () this.deleteTodo(item.id) }) } }, (item: Todo) item.id.toString()) } .width(100%) .layoutWeight(1) } .width(100%) .height(100%) .backgroundColor(#F5F5F5) } addTodo() { const text this.newTodoText.trim() if (text.length 0) return this.todos.push({ id: Date.now(), text: text, completed: false }) this.newTodoText } toggleTodo(id: number) { const index this.todos.findIndex(t t.id id) if (index 0) { this.todos[index].completed !this.todos[index].completed } } deleteTodo(id: number) { const index this.todos.findIndex(t t.id id) if (index 0) { this.todos.splice(index, 1) } } }3.2 子组件TodoItem实现Component struct TodoItem { Prop todo: Todo onToggle: () void onDelete: () void build() { Row() { // 复选框 Button({ type: ButtonType.Circle }) { if (this.todo.completed) { Text(✓) .fontSize(16) .fontColor(Color.White) } } .width(28) .height(28) .backgroundColor(this.todo.completed ? Color.Green : Color.White) .border({ width: 2, color: #CCC }) .onClick(() { this.onToggle() }) // 事项文本 Text(this.todo.text) .fontSize(18) .margin({ left: 12 }) .decoration({ type: this.todo.completed ? TextDecorationType.LineThrough : TextDecorationType.None }) .fontColor(this.todo.completed ? #BBB : #333) // 弹性空间 Blank() // 删除按钮 Button(✕) .fontSize(16) .fontColor(Color.Red) .backgroundColor(Color.Transparent) .onClick(() { this.onDelete() }) } .width(90%) .height(56) .padding({ left: 10, right: 10 }) .backgroundColor(Color.White) .borderRadius(8) .margin({ bottom: 8 }) } }3.3 关键代码解析ForEach列表渲染ForEach是ArkUI中用于遍历数组并生成组件列表的核心API。它接收三个参数数据源数组、组件生成函数和键值生成函数。键值生成函数第三个参数用于为每个列表项生成唯一标识帮助框架在数据变更时精确识别哪些项需要更新、添加或删除。ForEach(this.todos, (item: Todo) { ListItem() { TodoItem({ ... }) } }, (item: Todo) item.id.toString())Prop装饰器Prop用于父组件向子组件传递数据。与State不同Prop是单向数据流——子组件不能修改Prop的值只能读取。这种单向数据流的设计保证了数据变更的可预测性和可追踪性。事件回调传递子组件TodoItem通过回调函数onToggle和onDelete与父组件通信。这种模式是ArkUI中标准的子→父通信方式遵循了单向数据流的原则。条件渲染在复选框Button中使用if语句根据todo.completed状态决定是否显示✓标记。这是ArkUI中条件渲染的基本用法。四、HarmonyOS关键技术4.1 List组件与虚拟列表HarmonyOS的List组件是一个高性能的虚拟列表容器。当列表数据量较大时List只会渲染当前可见区域的列表项而不是一次性渲染所有数据。这种虚拟化渲染机制使得List可以轻松处理成千上万条数据的展示而不会出现性能问题。List() { ForEach(this.todos, ...) } .width(100%) .layoutWeight(1)List组件的关键特性包括自动虚拟化默认开启无需额外配置滚动优化支持平滑滚动和快速滚动缓存机制超出可视区域的列表项会被回收但保留最近回收的几项以便快速恢复4.2 ForEach的高效更新ForEach通过键值Key来追踪每个列表项的身份。当数据源数组发生变化时如添加、删除、修改ForEach通过比较新旧键值集合来确定具体的变化类型添加出现新的键值 → 创建新的组件实例删除键值消失 → 移除对应的组件实例更新键值存在但数据变化 → 复用组件实例并更新数据这种基于键值的差异比对算法Diff Algorithm确保了UI更新的高效性避免了不必要的组件重建。4.3 State的数组变更检测在ArkTS中State装饰的数组变量支持修改检测Mutation Detection。当使用数组的变更方法如push、splice、pop、shift、unshift等修改数组时框架能够检测到变化并触发UI更新。// 这些操作会触发UI更新 this.todos.push(newTodo) // 新增 this.todos.splice(index, 1) // 删除 this.todos[index].completed true // 修改数组中对象的属性需要注意的是直接给数组索引赋值如this.todos[0] newTodo可能不会触发UI更新此时应使用State配合扩展运算符创建新数组// 正确方式创建新数组替换旧数组 this.todos [...this.todos.slice(0, index), newTodo, ...this.todos.slice(index 1)]4.4 Text组件的文字装饰ArkUI的Text组件支持文字修饰效果包括下划线、删除线等Text(this.todo.text) .decoration({ type: TextDecorationType.LineThrough, // 删除线 color: #BBB // 线条颜色 })通过decoration属性我们可以为已完成的事项添加删除线效果这是待办清单应用中最经典的UI模式之一。五、UI设计与交互5.1 信息架构待办清单应用的信息架构遵循了清晰的从上到下的信息流标题 → 统计数据 → 输入区域 → 列表内容。这种布局结构符合用户的阅读习惯和操作预期。5.2 视觉反馈设计完成状态视觉反馈当用户点击复选框标记事项完成时该事项经历三个视觉变化复选框变为绿色并显示✓文字颜色从深色变为浅灰色#333 → #BBB文字添加删除线这三种视觉变化同时发生形成了一个强烈的已完成信号用户无需额外读取状态信息即可感知事项状态的变化。删除操作删除按钮使用红色✕符号符合通用的删除语义。按钮本身透明背景减少了视觉干扰。5.3 输入引导输入框中的占位符文本输入新的待办事项…为用户提供了明确的操作提示。同时添加按钮的enabled属性绑定到输入文本的长度检查当输入为空时按钮自动禁用从UI层面阻止了无效操作。5.4 颜色编码统计数据使用了颜色编码已完成数使用绿色积极/正向未完成数使用红色待处理/提醒。这种颜色编码使得统计数据一目了然。六、性能优化与最佳实践6.1 列表性能优化对于待办清单应用当待办事项数量增长到数百条时列表性能优化变得尤为重要1. 使用List而非ColumnList组件支持虚拟化渲染而Column会将所有子组件一次性渲染。对于动态列表始终优先使用List ForEach的组合。2. 合理的键值设计ForEach的键值生成函数应使用稳定且唯一的标识符如数据库ID、时间戳等。避免使用数组索引作为键值因为在插入或删除操作后索引会发生变化导致框架错误地复用组件实例。3. 避免复杂的列表项组件每个列表项TodoItem的组件层次应尽量简洁。过深的组件嵌套会增加布局计算的开销。6.2 状态更新优化批量更新当需要同时修改多个待办事项时如全部标记完成使用一个操作完成所有修改而非逐个修改// 优化前逐个修改触发多次渲染 this.todos.forEach(t { t.completed true }) // 优化后一次修改一次渲染 this.todos this.todos.map(t ({ ...t, completed: true }))不可变性尽量使用不可变数据更新模式即创建新的数组或对象来替代修改原数据。这有助于框架更高效地检测数据变化。6.3 输入防抖对于输入框的onChange事件如果需要在用户输入时执行某些操作如搜索过滤应考虑添加防抖Debounce处理// 简单的防抖实现 debounceInput setTimeout(() { // 执行操作 }, 300) // 在onChange中 onChange((value: string) { this.newTodoText value clearTimeout(this.debounceInput) this.debounceInput setTimeout(() { this.filterTodos(value) }, 300) })6.4 数据持久化当前应用的数据仅在内存中维护应用关闭后数据丢失。为了实现数据持久化可以使用HarmonyOS的Preferences或分布式数据管理能力// 使用 Preferences 存储数据 import dataPreferences from ohos.data.preferences async saveTodos(context: Context) { const prefs await dataPreferences.getPreferences(context, todo_prefs) await prefs.put(todos, JSON.stringify(this.todos)) await prefs.flush() } async loadTodos(context: Context) { const prefs await dataPreferences.getPreferences(context, todo_prefs) const jsonStr await prefs.get(todos, []) this.todos JSON.parse(jsonStr as string) }七、总结与扩展思路7.1 总结通过待办清单应用的开发实践我们深入学习了HarmonyOS ArkUI在数据管理方面的核心能力列表渲染掌握了List ForEach的组合用法和虚拟化原理状态管理深入理解了State、Prop装饰器的使用场景和单向数据流组件通信学习了通过回调函数实现子组件向父组件的通信CRUD操作实践了数据的新增、修改、删除和查询完整流程待办清单应用是数据驱动UI开发的经典范例它清晰地展示了如何将数据操作映射为UI变更是理解响应式编程思想的重要里程碑。7.2 扩展思路基于当前应用可以从以下方向进行功能增强1. 分类与标签允许用户为待办事项添加分类或标签如工作、生活、学习支持按分类筛选和查看。2. 优先级与排序添加优先级设置高/中/低支持按优先级、创建时间、截止时间等多种方式排序。3. 截止日期与提醒为待办事项设置截止日期利用HarmonyOS的定时任务能力在截止时间前发送通知提醒。4. 拖拽排序通过手势识别实现列表项的拖拽排序让用户可以自定义事项的排列顺序。// 拖拽排序示例 ListItem() .gesture( PanGesture() .onActionUpdate((event: GestureEvent) { // 处理拖拽逻辑 }) )5. 搜索功能添加搜索栏支持按关键字搜索待办事项。6. 数据导出与分享支持将待办清单导出为文本文件或分享到其他应用。7. 云同步利用HarmonyOS的分布式数据服务实现待办清单在手机、平板、手表等设备间的实时同步。7.3 架构演进建议当待办清单应用的功能复杂度提升时建议采用以下架构演进策略引入ViewModel层将数据逻辑从UI组件中抽离形成独立的ViewModel层提高代码的可测试性和可维护性使用Provide/Consume在深层嵌套的组件树中使用Provide和Consume装饰器替代逐层传递回调函数模块化拆分将统计栏、输入区域、列表区域拆分为独立的模块每个模块职责单一、可独立测试待办清单应用是HarmonyOS开发学习路径上的一个重要节点它连接了基础UI知识和复杂数据管理能力。希望通过本篇博文的深入分析能够帮助你建立起扎实的ArkTS数据管理基础和良好的组件化设计思维。