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 跨组件通信

相关推荐
金金金__1 小时前
一篇文章带你掌握SpecKit
后端
Hyyy1 小时前
流式渲染的基础——SSE
前端·后端·面试
Gopher_HBo2 小时前
docker网络学习
后端
达达尼昂2 小时前
在 Claude Cowork 中用好 Claude Fable 5
android·人工智能·后端
HONG````2 小时前
HarmonyOS ArkUI Button 组件深度解析:类型、状态与自定义样式
后端
ThanksGive2 小时前
TurboLock:从 v1 Redis 分布式锁到 v2 Go 并发控制中间件
分布式·后端
贺国亚2 小时前
特征平台-Feature-Store设计
后端
杨运交3 小时前
[049][Crypto模块]前后端混合加密API实战:基于Spring Boot的AES+RSA安全传输方案
spring boot·后端·安全
geovindu3 小时前
CSharp: Recursion Algorithm
开发语言·后端·算法·c#·递归算法