及时做APP开发实战(十三)-已完成数量统计修复
本文将介绍如何修复主页面已完成数量不更新的BUG,并深入分析ArkTS状态管理机制。
一、问题分析
1.1 问题现象
图 1 问题现象-不及时更新已完成数量

┌─────────────────────────────────────┐
│ 待办列表 🌙 │
├─────────────────────────────────────┤
│ ┌─────────────────────────────┐ │
│ │ 进行中 已完成 全部 │ │
│ │ 3 0❌ 5 │ │ ← 已完成数量不更新
│ └─────────────────────────────┘ │
│ │
│ ☐ 学习ArkTS │
│ ☐ 开发及时做APP │
│ ✓ 写博客文档 ← 已完成 │
│ ✓ 代码优化 ← 已完成 │
│ ☐ 测试功能 │
└─────────────────────────────────────┘
1.2 问题原因
问题代码:
┌─────────────────────────────────────┐
│ Text(this.todoViewModel │
│ .getCompletedTodoCount() │
│ .toString()) │
└─────────────────────────────────────┘
↓
调用ViewModel的方法获取数据
↓
❌ 不是@State变量,UI不会自动刷新
二、ArkTS状态管理机制
2.1 状态变量装饰器
| 装饰器 | 作用 | 触发UI更新 |
|---|---|---|
| @State | 组件内状态 | ✅ 自动更新 |
| @Prop | 父→子单向传递 | ✅ 自动更新 |
| @Link | 父↔子双向绑定 | ✅ 自动更新 |
| @Provide | 跨组件提供 | ✅ 自动更新 |
| @Consume | 跨组件消费 | ✅ 自动更新 |
2.2 为什么ViewModel方法不触发更新
typescript
// ❌ 问题代码
@Component
struct Index {
@State todoList: TodoItem[] = [];
private todoViewModel: TodoViewModel = new TodoViewModel();
build() {
Column() {
// 调用ViewModel方法
Text(this.todoViewModel.getCompletedTodoCount().toString())
// ↑ 不是@State变量,值变化时UI不更新
}
}
}
// ✅ 正确代码
@Component
struct Index {
@State todoList: TodoItem[] = [];
build() {
Column() {
// 基于@State变量计算
Text(this.todoList.filter(item => item.completed).length.toString())
// ↑ 依赖@State变量,值变化时UI自动更新
}
}
}
2.3 状态更新流程
┌─────────────────────────────────────┐
│ 状态更新流程 │
├─────────────────────────────────────┤
│ 1. @State变量被修改 │
│ ↓ │
│ 2. ArkTS检测到变化 │
│ ↓ │
│ 3. 触发组件重新渲染 │
│ ↓ │
│ 4. 重新计算依赖该变量的表达式 │
│ ↓ │
│ 5. UI更新显示 │
└─────────────────────────────────────┘
三、解决方案
3.1 修改前代码
typescript
// 统计信息组件
@Builder
StatisticsBar() {
Row() {
Column() {
// ❌ 调用ViewModel方法
Text(this.todoViewModel.getActiveTodoCount().toString())
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#FF6B6B')
Text('进行中')
.fontSize(12)
.fontColor('#666666')
}
.layoutWeight(1)
Column() {
// ❌ 调用ViewModel方法
Text(this.todoViewModel.getCompletedTodoCount().toString())
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#4CAF50')
Text('已完成')
.fontSize(12)
.fontColor('#666666')
}
.layoutWeight(1)
Column() {
// ✅ 基于@State变量
Text(this.todoList.length.toString())
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#2196F3')
Text('全部')
.fontSize(12)
.fontColor('#666666')
}
.layoutWeight(1)
}
}
3.2 修改后代码
typescript
// 统计信息组件
@Builder
StatisticsBar() {
Row() {
Column() {
// ✅ 基于@State变量计算
Text(this.todoList.filter(item => !item.completed).length.toString())
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#FF6B6B')
Text('进行中')
.fontSize(12)
.fontColor('#666666')
}
.layoutWeight(1)
Column() {
// ✅ 基于@State变量计算
Text(this.todoList.filter(item => item.completed).length.toString())
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#4CAF50')
Text('已完成')
.fontSize(12)
.fontColor('#666666')
}
.layoutWeight(1)
Column() {
// ✅ 基于@State变量
Text(this.todoList.length.toString())
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#2196F3')
Text('全部')
.fontSize(12)
.fontColor('#666666')
}
.layoutWeight(1)
}
}
3.3 关键改动
| 修改项 | 修改前 | 修改后 |
|---|---|---|
| 进行中数量 | todoViewModel.getActiveTodoCount() |
todoList.filter(item => !item.completed).length |
| 已完成数量 | todoViewModel.getCompletedTodoCount() |
todoList.filter(item => item.completed).length |
| 全部数量 | todoList.length |
todoList.length(不变) |
四、验证修复
4.1 测试步骤
1. 启动应用,进入主页面
↓
2. 查看统计栏显示:进行中 X,已完成 0,全部 X
↓
3. 点击待办项,标记为已完成
↓
4. 观察统计栏:已完成数量应该 +1
↓
5. 再次点击,取消完成状态
↓
6. 观察统计栏:已完成数量应该 -1
4.2 预期结果
┌─────────────────────────────────────┐
│ 统计栏正确更新 │
├─────────────────────────────────────┤
│ 操作前:进行中 3,已完成 0,全部 3 │
│ 标记完成:进行中 2,已完成 1,全部 3 │
│ 再标记完成:进行中 1,已完成 2,全部 3 │
│ 取消完成:进行中 2,已完成 1,全部 3 │
└─────────────────────────────────────┘
图 2 修复后效果

五、最佳实践总结
5.1 状态管理原则
┌─────────────────────────────────────┐
│ ✅ 状态管理原则 │
├─────────────────────────────────────┤
│ □ UI显示的数据应依赖@State变量 │
│ □ 避免在build()中调用非状态方法 │
│ □ ViewModel方法用于业务逻辑,不直接用于UI显示 │
│ □ 使用计算属性/表达式基于@State计算 │
└─────────────────────────────────────┘
5.2 常见错误模式
typescript
// ❌ 错误模式1:调用方法获取数据
Text(this.getData().toString())
// ❌ 错误模式2:访问非状态变量
Text(this.count.toString()) // count不是@State
// ✅ 正确模式:基于@State计算
Text(this.todoList.filter(...).length.toString())
六、总结
本文修复了主页面已完成数量不更新的BUG,核心要点:
- 问题原因:调用ViewModel方法不触发UI更新
- 解决方案:基于@State变量计算显示值
- 关键原理:ArkTS状态管理机制,只有@State变量变化才触发UI更新