装饰器 | 作用 |
---|---|
@Observed | 标记一个类为可观察对象,当类属性变化时触发UI更新(需配合@ObjectLink 使用) |
@ObjectLink | 在自定义组件中绑定被@Observed 装饰的类实例,实现对象属性级别的响应式更新 |
二、典型使用场景
1. 嵌套对象属性更新
场景:
当需要监听一个对象内部属性的变化时(如用户信息中的 address.city
),直接使用 @State
无法触发更新,需借助 @Observed
+ @ObjectLink
。
示例代码:
// 定义可观察类
@Observed
class User {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
// 父组件
@Entry
@Component
struct ParentPage {
@State user: User = new User('Alice', 25);
build() {
Column() {
ChildComponent({ user: this.user }) // 传递对象
Button('Increase Age')
.onClick(() => {
this.user.age++; // 修改嵌套属性
})
}
}
}
// 子组件
@Component
struct ChildComponent {
@ObjectLink user: User; // 绑定对象
build() {
Text(`Age: ${this.user.age}`) // 自动更新
}
}
2. 数组元素属性更新
场景:
监听数组中某个对象的属性变化(如待办列表项的完成状态)。
示例代码:
@Observed
class TodoItem {
id: number;
text: string;
completed: boolean;
constructor(id: number, text: string) {
this.id = id;
this.text = text;
this.completed = false;
}
}
@Entry
@Component
struct TodoList {
@State todos: TodoItem[] = [
new TodoItem(1, 'Buy milk'),
new TodoItem(2, 'Read book')
];
build() {
Column() {
ForEach(this.todos, (item: TodoItem) => {
TodoItemComponent({ item: item })
})
}
}
}
@Component
struct TodoItemComponent {
@ObjectLink item: TodoItem;
build() {
Row() {
Text(this.item.text)
.decoration({ type: this.item.completed ? TextDecorationType.LineThrough : TextDecorationType.None })
Toggle({ type: ToggleType.Checkbox })
.isOn(this.item.completed)
.onChange((checked: boolean) => {
this.item.completed = checked; // 直接修改数组元素属性
})
}
}
}
3. 跨组件共享对象状态
场景:
多个子组件需要共享并修改同一个对象的属性(如购物车的商品数量)。
示例代码:
@Observed
class CartItem {
id: number;
name: string;
quantity: number;
constructor(id: number, name: string) {
this.id = id;
this.name = name;
this.quantity = 1;
}
}
@Entry
@Component
struct ShoppingCart {
@State item: CartItem = new CartItem(1, 'iPhone');
build() {
Column() {
Text(`Total: ${this.item.quantity}`)
QuantityController({ item: this.item })
AddToCartButton({ item: this.item })
}
}
}
// 数量控制器组件
@Component
struct QuantityController {
@ObjectLink item: CartItem;
build() {
Row() {
Button('-')
.onClick(() => this.item.quantity--)
Text(`${this.item.quantity}`)
Button('+')
.onClick(() => this.item.quantity++)
}
}
}
// 加入购物车按钮组件
@Component
struct AddToCartButton {
@ObjectLink item: CartItem;
build() {
Button('Add to Cart')
.onClick(() => {
this.item.quantity = 1; // 重置数量
})
}
}
三、与其他状态管理方案对比
方案 | 适用场景 | 对象属性监听 | 代码复杂度 |
---|---|---|---|
@State | 简单值类型(string/number) | ❌ | ⭐ |
@Link | 父子组件间的双向绑定 | ❌ | ⭐⭐ |
@Observed + @ObjectLink | 嵌套对象/数组元素属性更新 | ✅ | ⭐⭐⭐ |
AppStorage | 全局简单状态 | ❌ | ⭐⭐ |
四、关键注意事项
-
必须成对使用:
@ObjectLink
必须绑定被@Observed
装饰的类实例。
-
避免循环引用:
@Observed class Node { child?: Node; // 危险!可能导致循环引用 }
-
不可直接替换对象:
// 错误!@ObjectLink 不能重新赋值 this.user = new User('Bob', 30); // 正确:修改属性 this.user.name = 'Bob'; this.user.age = 30;
-
性能优化:
-
对于深层次嵌套对象,建议使用
@Track
装饰需要监听的特定属性:@Observed class User { name: string; @Track age: number; // 仅监听 age 变化 }
-
五、常见问题解决
问题:修改了对象属性但UI未更新
排查步骤:
- 确认类是否被
@Observed
装饰 - 检查是否使用
@ObjectLink
绑定对象 - 确保直接修改属性值(而非替换整个对象)
- 使用
async
函数时,确认在TaskPool
或主线程操作
通过合理使用 @Observed
和 @ObjectLink
,可以实现鸿蒙应用中复杂数据结构的精准响应式更新。