文章

ArkTS 状态管理深度解析

ArkTS 状态管理:@State、@Prop、@Link、@Provide/@Consume 等装饰器的数据流。 讲清单向数据流与组件联动,掌握后能答出「ArkTS 状态怎么从父传到子再回传」。

ArkTS 状态管理深度解析

一句话概括

ArkTS 用 5 个装饰器覆盖所有状态流转场景:@State(组件私有)→ @Prop(父传子单向)→ @Link(父子双向)→ @Provide/@Consume(跨层级广播),核心思想是通过装饰器声明数据流向,框架自动追踪变更并更新 UI。

核心知识点

1. @State——组件私有状态的基石

1
2
3
4
5
6
7
8
9
10
11
12
13
@Component
struct Counter {
  @State count: number = 0;  // 组件内部状态,变更自动刷新 UI

  build() {
    Column() {
      Text(`计数: ${this.count}`).fontSize(24)
      Button('+1').onClick(() => this.count++)  // 直接改,UI 自动更新
    }
  }
}
// @State 能观测到第一层属性变化(基础类型赋值、对象属性修改)
// 深层嵌套对象需搭配 @Observed 装饰器类

2. @Prop——父传子单向数据流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Component
struct ChildLabel {
  @Prop label: string;  // 父组件传入,子组件不能改原值
  @Prop active: boolean = false;

  build() {
    Text(this.label)
      .fontColor(this.active ? '#007AFF' : '#999')
  }
}

// 父组件:ChildLabel({ label: this.title, active: this.isSelected })
// 子组件内修改 this.label 不会影响父组件
// 要回传数据?通过回调函数 onXXX

3. @Link——父子双向同步

1
2
3
4
5
6
7
8
9
10
11
12
@Component
struct ChildToggle {
  @Link checked: boolean;  // 双向绑定,子和父共享同一份数据

  build() {
    Toggle({ type: ToggleType.Switch, isOn: this.checked })
      .onChange((isOn: boolean) => this.checked = isOn)
  }
}

// 父组件:ChildToggle({ checked: $isDarkMode })
//                         ↑ 注意 $ 符号,表示"传引用绑定"而非传值

4. @Provide/@Consume——跨层级免传参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 顶层组件
@Component
struct AppRoot {
  @Provide('theme') theme: string = 'dark';  // 向下广播

  build() { Column() { MainPage() } }
}

// 任意深层子组件(无需通过中间层逐级传递)
@Component
struct DeepChild {
  @Consume('theme') theme: string;  // 无论多深,直接拿到

  build() {
    Text(`当前主题: ${this.theme}`)
  }
}
// 类比 React Context / Vue provide-inject,解决 props 逐层透传的痛点

5. 五个装饰器的选择决策树

1
2
3
4
5
数据只在当前组件用?                      → @State
父传子、子只读不写?                      → @Prop
父传子、子需要修改且同步回父?              → @Link(调用时加 $)
祖先后代跨多层,中间组件不需要知道?          → @Provide/@Consume
复杂对象且需要深层属性变更触发更新?         → @Observed + @ObjectLink

其实你每天都在用

  • 开关/复选框:@State isOn: boolean → 点击切换后 UI 自动刷新
  • 表单输入:@State username: string → TextInput 双向绑定,输入即更新
  • 主题切换:@Provide theme 在根组件 → @Consume theme 在任意深层组件 → 改一处全页面变色
  • 列表项状态:@Link selectedId → 点击某行高亮,父子组件数据实时同步
  • Tab 切换:@State currentTab: number → 切 Tab 时所有依赖该状态的组件自动刷新

常见误解(FAQ)

  • ❌ 误区:「@Prop 修改后父组件也能感知」 @Prop 是单向快照,子改了自己的副本不等于父变了。需要回写用 @Link(双向)或者回调函数通知父。

  • ❌ 误区:「@State 能自动观测对象深层嵌套属性」 不能。@State user: User → 只监听 this.user = newUser 这种整体替换,this.user.name = 'new' 不会触发更新。需要把 User 类标记为 @Observed,属性用 @ObjectLink 接收。

  • ❌ 误区:「@Link 绑定时忘加 $ 也能用」 不加 $ 传的是值拷贝,子改了自己那份但父不知道——编译器不会报错但你 debug 到怀疑人生。记住口诀:见 @Link 必有 $。

  • ❌ 误区:「@Provide/@Consume 是全局的,任何组件都能访问」 作用域限制在组件树内,不是应用全局。祖先 provide 后只有它的子孙组件能 consume,兄弟组件或无关组件拿不到。全局共享建议用 AppStorage。

一句话总结

ArkTS 状态管理的精髓就一句话:装饰器声明数据流向,框架追踪变更刷新 UI——你只管声明”这个数据属于谁、往哪流”,剩下的框架来做。

本文由作者按照 CC BY 4.0 进行授权