HarmonyOS ArkTS 实战:用 @State 状态管理做一个待办清单页面

举报
yd_273651526 发表于 2026/11/10 13:25:55 2026/11/10
【摘要】 对刚接触 HarmonyOS 的开发者来说,最快理解 ArkUI 声明式开发的方式,不是先啃完所有概念,而是亲手做一个能跑起来的小页面。待办清单正好合适:它包含输入、列表渲染、状态变更和删除操作,麻雀虽小,五脏俱全。本文用 ArkTS 实现一个待办清单页面,并借此讲清 @State 的工作方式和几个新手最容易踩的坑。 一、准备工作开发工具:DevEco Studio(建议使用与目标 SDK ...

对刚接触 HarmonyOS 的开发者来说,最快理解 ArkUI 声明式开发的方式,不是先啃完所有概念,而是亲手做一个能跑起来的小页面。待办清单正好合适:它包含输入、列表渲染、状态变更和删除操作,麻雀虽小,五脏俱全。本文用 ArkTS 实现一个待办清单页面,并借此讲清 @State 的工作方式和几个新手最容易踩的坑。

一、准备工作

  • 开发工具:DevEco Studio(建议使用与目标 SDK 匹配的版本)
  • 新建项目时选择 Empty Ability 模板,语言选 ArkTS
  • 页面文件位于 entry/src/main/ets/pages/Index.ets

二、先定义数据结构

待办事项有三个属性:唯一 id、标题和是否完成。用 interface 把结构先定下来,后面的状态操作会清晰很多:

interface TodoItem {
  id: number
  title: string
  done: boolean
}

三、编写页面组件

ArkUI 是声明式 UI:你描述“界面应该长什么样”,框架负责在数据变化时刷新界面。驱动刷新的关键就是状态变量——用 @State 装饰的变量发生变化时,使用它的 UI 会自动重新渲染。

@Entry
@Component
struct TodoPage {
  @State private todos: TodoItem[] = [
    { id: 1, title: '熟悉 ArkTS 基本语法', done: true },
    { id: 2, title: '完成待办清单页面', done: false }
  ]
  @State private draft: string = ''
  private nextId: number = 3

  private addTodo(): void {
    const title = this.draft.trim()
    if (title.length === 0) {
      return
    }
    this.todos = [...this.todos, { id: this.nextId, title: title, done: false }]
    this.nextId += 1
    this.draft = ''
  }

  private toggleTodo(id: number): void {
    this.todos = this.todos.map((item: TodoItem): TodoItem => {
      if (item.id === id) {
        return { id: item.id, title: item.title, done: !item.done }
      }
      return item
    })
  }

  private removeTodo(id: number): void {
    this.todos = this.todos.filter((item: TodoItem): boolean => item.id !== id)
  }

  build() {
    Column({ space: 12 }) {
      Text('我的待办')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)

      Row({ space: 8 }) {
        TextInput({ placeholder: '输入待办事项', text: this.draft })
          .layoutWeight(1)
          .onChange((value: string) => {
            this.draft = value
          })
        Button('添加')
          .onClick(() => {
            this.addTodo()
          })
      }
      .width('100%')

      List({ space: 6 }) {
        ForEach(this.todos, (item: TodoItem) => {
          ListItem() {
            Row({ space: 8 }) {
              Checkbox()
                .select(item.done)
                .onChange(() => {
                  this.toggleTodo(item.id)
                })
              Text(item.title)
                .layoutWeight(1)
                .decoration({ type: item.done ? TextDecorationType.LineThrough : TextDecorationType.None })
                .fontColor(item.done ? '#999999' : '#222222')
              Button('删除')
                .type(ButtonType.Normal)
                .onClick(() => {
                  this.removeTodo(item.id)
                })
            }
            .width('100%')
          }
        }, (item: TodoItem): string => item.id.toString())
      }
      .layoutWeight(1)
      .width('100%')
    }
    .padding(16)
    .width('100%')
    .height('100%')
  }
}

四、三个关键点

1. @State 只追踪“赋值”,不追踪“原地修改”

这是新手最常见的问题:直接写 this.todos.push(newItem) 或 item.done = true,界面有时不会刷新。原因是框架对状态的观测以变量重新赋值最可靠。稳妥的写法是生成新数组再赋值,比如添加时用展开语法 [...this.todos, newItem],切换完成状态时用 map 返回新对象。养成“不可变更新”的习惯,后面学 @Prop、@Link 时也会受益。

2. ForEach 的第三个参数是键值生成器,别省略

ForEach 渲染列表时靠 key 判断哪些元素是新增、删除或复用。上面用待办的 id 作为 key,这样勾选、删除时框架能准确定位每一项,不会出现勾选状态串行的问题。用数组下标当 key 在增删场景下容易出错,尽量用数据的唯一标识。

3. 输入框要用状态变量双向承接

TextInput 的 text 参数绑定 draft,再在 onChange 里把最新输入写回 draft,这样点击“添加”时才能拿到用户真正输入的内容。添加完成后把 draft 清空,输入框也会随之刷新为空。

五、可以继续扩展的方向

这个页面跑通之后,可以顺着下面的方向练手,每一步都是一个真实的知识点:

  • 数据持久化:用首选项(Preferences)或关系型数据库保存待办,重启应用不丢失
  • 拆分组件:把单个待办行抽成 @Component 子组件,用 @Prop 接收数据、回调通知父组件
  • 多页面跳转:加一个详情页,用路由传参,练习页面间数据传递

总结

待办清单虽小,却完整走了一遍 ArkUI 的核心思路:用 @State 管理页面状态,用声明式语法描述界面,用不可变更新保证刷新可靠。把这一页写明白,再去看更复杂的状态装饰器和组件通信,就不会觉得抽象了。建议动手敲一遍,并故意把 toggleTodo 改成原地修改试试,直观感受一下状态观测的边界——这种“踩坑式学习”比只看文档记得牢得多。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。