【HarmonyOS开发小实践】ArkUI 组件内状态@State、@Prop、@Link 的父子传递
从最基础的 @State 开始
@State 是组件自己的状态,只有声明它的组件能读取和修改。它是所有状态管理的起点。
@Entry
@Component
struct Counter {
@State count: number = 0; // 组件私有状态
build() {
Column({ space: 16 }) {
Text(`计数:${this.count}`)
Button('+1')
.onClick(() => {
this.count++; // 修改状态,UI 自动刷新
})
}
}
}
@State 的三个特点:
- 只能在声明它的组件内修改
- 修改后引用它的 UI 自动刷新
- 支持的类型有 number、string、boolean、enum,以及对象、数组(配合
@Observed等)
@State 的复杂类型
当 @State 修饰的是对象或数组,直接替换或修改其属性都能触发刷新:
@Entry
@Component
struct StateObjectDemo {
// 对象类型的状态
@State user: User = new User('张三', 25);
build() {
Column({ space: 16 }) {
Text(`姓名:${this.user.name},年龄:${this.user.age}`)
Button('改年龄')
.onClick(() => {
this.user.age++; // 修改对象属性,能触发刷新
})
Button('替换对象')
.onClick(() => {
this.user = new User('李四', 30); // 整体替换,也能触发
})
}
}
}
class User {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
注意:普通 class 的 @State 修改属性能刷新,但嵌套对象(对象里的对象)的深层属性修改,需要 @Observed 配合,否则不刷新。这是常见困惑点,稍后展开。
@Prop:父传子,单向
@Prop 让父组件把值传给子组件,子组件修改不影响父组件:
// 父组件
@Entry
@Component
struct Parent {
@State parentValue: number = 10;
build() {
Column({ space: 16 }) {
Text(`父组件的值:${this.parentValue}`)
// 把 parentValue 传给子组件
Child({ childValue: this.parentValue })
}
}
}
// 子组件
@Component
struct Child {
@Prop childValue: number; // 接收父组件传入的值(单向)
build() {
Column({ space: 8 }) {
Text(`子组件收到的值:${this.childValue}`)
Button('子组件 +1')
.onClick(() => {
this.childValue++; // 只改子组件的副本,不影响父组件
})
}
}
}
@Prop 是值传递的副本,子组件改的是自己那份,父组件不知道。适合"父组件的数据子组件只读展示,子组件可以基于它做本地调整"的场景。
@Link:父子双向同步
@Link 让父子组件共享同一份数据,改任一边都同步到另一边:
// 父组件
@Entry
@Component
struct Parent {
@State parentValue: number = 10;
build() {
Column({ space: 16 }) {
Text(`父组件的值:${this.parentValue}`)
// 注意:传参时用 $ 前缀,表示传递引用
Child({ childValue: $parentValue })
}
}
}
// 子组件
@Component
struct Child {
@Link childValue: number; // 双向绑定的引用
build() {
Column({ space: 8 }) {
Text(`子组件看到的值:${this.childValue}`)
Button('子组件 +1')
.onClick(() => {
this.childValue++; // 父子同步修改
})
}
}
}
关键在父组件传参时加 $ 前缀:Child({ childValue: $parentValue })。这样父子指向同一份数据,双向同步。
@Prop vs @Link 的对比
@Prop:单向,子组件拿到副本
父 [10] ──复制──> 子 [10]
子改自己的 [11],父还是 [10]
@Link:双向,子组件拿到引用
父 [$parentValue] <────> 子 [$childValue]
指向同一份数据,任一边改,两边都是 [11]
用一张表总结选择依据:
| 场景 | 用哪个 |
|---|---|
| 子组件只展示父组件数据,不改 | @Prop |
| 子组件要修改父组件的数据 | @Link |
| 简单值(数字、字符串)子组件可本地修改 | @Prop |
| 表单类,子组件的输入要同步回父 | @Link |
反过来讲,如果子组件需要改父组件的数据,却用了 @Prop,那是个 bug——子组件改了父组件不会更新。
@Watch:监听状态变化
@Watch 给状态加观察器,状态变化时执行回调:
@Entry
@Component
struct WatchDemo {
@State @Watch('onCountChange') count: number = 0;
build() {
Column({ space: 16 }) {
Text(`计数:${this.count}`)
Button('+1')
.onClick(() => this.count++)
}
}
// 状态变化时的回调
onCountChange(propertyName: string, newValue: number, oldValue: number) {
console.info(`count 从 ${oldValue} 变成 ${newValue}`);
if (newValue >= 10) {
console.info('达到 10 了,触发提示');
}
}
}
@Watch('onCountChange') 里的字符串是回调方法名。常见用途:
- 输入校验(值变化时验证合法性)
- 联动逻辑(一个状态变化触发另一个状态更新)
- 埋点(值变化时记录日志)
深坑:嵌套对象的修改不刷新
前面提过,普通 @State 的对象,修改嵌套对象属性不会触发刷新:
class Address {
city: string;
constructor(city: string) {
this.city = city;
}
}
class Person {
name: string;
address: Address; // 嵌套对象
constructor(name: string, address: Address) {
this.name = name;
this.address = address;
}
}
@Entry
@Component
struct NestedDemo {
@State person: Person = new Person('张三', new Address('北京'));
build() {
Column({ space: 16 }) {
Text(`${this.person.name} 在 ${this.person.address.city}`)
Button('改城市')
.onClick(() => {
this.person.address.city = '上海'; // 修改嵌套对象,不刷新!
})
}
}
}
上面按钮点了 Text 不会更新,因为 address 是普通对象,框架监听不到它内部属性的变化。
解决办法是用 @Observed + @ObjectLink:
@Observed // 标记类,可被观察
class Address {
city: string;
constructor(city: string) {
this.city = city;
}
}
@Observed
class Person {
name: string;
address: Address;
constructor(name: string, address: Address) {
this.name = name;
this.address = address;
}
}
@Entry
@Component
struct NestedDemo {
@State person: Person = new Person('张三', new Address('北京'));
build() {
Column({ space: 16 }) {
Text(`${this.person.name} 在 ${this.person.address.city}`)
Button('改城市')
.onClick(() => {
this.person.address.city = '上海'; // 现在能刷新了
})
}
}
}
@Observed 让类变成"可观察的",@ObjectLink 用在子组件接收 @Observed 对象时。这个组合负责处理嵌套对象的深层属性修改。
小小建议哦
嵌套对象改属性不刷新是状态管理里最隐蔽的问题之一。两个处理方式:
- 用
@Observed/@ObjectLink:正解,从根上让嵌套对象可观察 - 整体替换:
this.person = new Person(this.person.name, new Address('上海')),绕开深层修改
短期可以用方式 2 快速解决,长期和团队规范应该用方式 1,否则代码里到处是别扭的整体替换。
更多推荐



所有评论(0)