HarmonyOS 状态管理:@State、@Prop、@Link、@Watch

适用版本 :HarmonyOS 6.1(API 12)及以上 验证环境 :Pura 90 Pro 模拟器(HarmonyOS 6.1.1,API 24) 关键概念@State@Prop@Link@Watch、单向数据流、双向绑定


前言

ArkUI 的状态管理基于响应式模型:状态变量变化时,UI 自动重新渲染关联的组件树。四个核心装饰器各有明确的数据流方向:@State(私有)→ @Prop(单向)→ @Link(双向),@Watch 负责观测变化并触发副作用。


一、@State --- 组件私有状态

typescript 复制代码
@Entry
@Component
struct CounterPage {
  @State count: number = 0  // 私有,只有本组件可读写

  build() {
    Column({ space: 12 }) {
      Text(`count = ${this.count}`)
        .fontSize(28).fontWeight(FontWeight.Bold)
      Row({ space: 12 }) {
        Button('-').onClick(() => { this.count-- })
        Button('+').onClick(() => { this.count++ })
      }
    }
  }
}

规则

  • @State 变量变化 → ArkUI 自动重新渲染依赖它的 UI 区域
  • 外部组件不能 直接读写 @State 变量

二、@Prop --- 父传子(单向)

typescript 复制代码
@Component
struct ChildDisplay {
  @Prop count: number = 0  // 父传来的副本,子不能写回父

  build() {
    Text(`子组件显示: ${this.count}`).fontColor('#666')
  }
}

// 父组件使用
ChildDisplay({ count: this.count })  // 父 count 变 → 子自动刷新

规则

  • @State 变化 → 子 @Prop 自动同步
  • 子组件修改 @Prop → 只改子组件自己的副本,不影响父

typescript 复制代码
@Component
struct SharedCounter {
  @Link sharedCount: number  // 不设默认值,从父引用

  build() {
    Column({ space: 8 }) {
      Text(`@Link sharedCount = ${this.sharedCount}`)
      Button('子组件修改 @Link +1')
        .onClick(() => { this.sharedCount++ })  // 同步回父!
    }
  }
}

// 父组件
@State sharedCount: number = 0

// 使用时传 $(引用符号)
SharedCounter({ sharedCount: $sharedCount })

规则

  • @Link 不设默认值,必须从父组件接收
  • 子修改 @Link → 立即反映到父组件的 @State,父 UI 同步刷新

四、@Watch --- 状态变化监听

typescript 复制代码
@Entry
@Component
struct WatchDemo {
  @Watch('onQueryChange')  // watchTarget 变化时调用 onQueryChange
  @State watchTarget: string = ''

  @State log: string[] = []

  onQueryChange(): void {
    // 副作用:触发搜索、请求 API、发出事件等
    this.log = [...this.log.slice(-4), `变化 → "${this.watchTarget}"`]
  }

  build() {
    Column() {
      TextInput({ placeholder: '输入触发 @Watch' })
        .onSubmit(() => { this.watchTarget = this.inputVal })
      ForEach(this.log, (l: string) => {
        Text(l).fontColor('#9b59b6')
      })
    }
  }
}

规则

  • @Watch('方法名') 声明在状态变量前,当该变量变化时调用对应方法
  • 回调方法不接受参数(用 this.xxx 读取新旧值)
  • 适合触发 API 请求、日志记录等副作用

五、四者对比

装饰器 数据流向 修改权 适用场景
@State 内部 只有自己 组件私有计数器、开关、表单状态
@Prop 父→子 子仅本地 展示父数据,子不需要回写
@Link 父↔子 双方均可 子组件需要修改父状态(如弹窗关闭)
@Watch 观察任意 @State 无(只监听) 状态变化时触发副作用

模拟器运行截图

初始状态


交互后状态

点击「+」按钮增加计数、子组件修改 @Link,以及输入内容触发 @Watch 后的显示状态。


常见问题

Q:为什么修改 @State 对象的属性(如 this.user.name = 'xxx')没有触发 UI 刷新? A:ArkTS 的响应式检测粒度是赋值,而不是属性修改。要触发刷新,需要整体替换对象:

typescript 复制代码
// ❌ 不触发刷新
this.user.name = 'xxx'

// ✅ 触发刷新(整体替换)
const updated = new User()
updated.name = 'xxx'
updated.age = this.user.age
this.user = updated

Q:@Prop 可以是数组吗? A:可以。@Prop list: string[] = [] 有效,但同样遵循单向规则:子组件修改数组内容不会回写父组件。

Q:@Watch 的回调里可以再修改被 watch 的变量吗? A:可以,但要避免无限循环(修改触发回调,回调再修改,再触发回调...)。通常在回调内加条件判断确保幂等。


上一篇:ArkUI 布局系统 下一篇:@Provide/@Consume 跨组件通信

相关推荐
用户83562907805118 小时前
使用 Python 为 PowerPoint 演示文稿添加评论
后端·python
jvmind_dev19 小时前
SWT 堆外内存泄漏排查实录:一次 PNG 保存泄漏一张图,一行 g_free 治好
java·后端
南雨北斗19 小时前
TP6 安全规范的接收post请求与安全机制分析
后端
南雨北斗19 小时前
TP6 防范XSS攻击 响应头防御(设置内容安全策略 :CSP)
后端
简单风19 小时前
不要再人工一行一行的 review AI 生成的代码了
后端
大勇前进19 小时前
虚拟线程避坑全解:Thread Pinning 问题定位与 3 种主流解决方案对比
后端
嘻哈baby19 小时前
Go 语言为什么不在语言层面保证 map 线程安全?
后端
大黄评测19 小时前
GraalVM 原生镜像构建实战:反射配置策略与 Spring Boot AOT 编译常见坑点
后端
杨杨杨大侠19 小时前
MCP、ToolFunction 与 Skill:从模型输入到工具执行
后端·aigc·openai
ClouGence19 小时前
2026 数据库 CI/CD 工具大盘点:4 款热门工具怎么选?
数据库·后端·ci/cd