一、基本概念
1. @Provide与@Consume:跨组件树双向同步
这对装饰器实现了任意层级组件间的双向数据绑定。@Provide在祖先组件中声明共享状态,@Consume在后代组件中消费该状态,形成类似"发布-订阅"的机制。其突破传统父子传参的限制,支持组件树中任意层级的通信。
- 自动广播机制:@Provide变量变更时,所有关联@Consume组件自动更新
- 别名匹配规则:支持通过相同变量名或别名建立绑定(如@Provide('count')与@Consume('count'))
- 类型严格校验:要求@Provide与@Consume变量类型完全一致
- 支持
Object
、Class
、Array
等复杂类型
2. @Observed与@ObjectLink:深度数据观察
这对装饰器专为解决嵌套对象/数组的深度监听问题而设计。传统@State只能感知对象引用的变化,而@Observed通过代理机制实现对类属性的细粒度观察,@ObjectLink则建立与父组件@Observed实例的双向绑定。
- 支持多层嵌套属性变更(如user.address.city)
- 支持
Map
、Set
等复杂数据结构(需API 11+) - 可观察数组元素的增删改操作, 数组操作需保持引用(如
this.order.items = [...this.order.items]
) - 类对象属性的原子级更新触发UI刷新
- 原子同步:@ObjectLink建立与父组件@Observed实例的直接引用关系,避免整体对象替换的性能损耗
- 批量更新:使用
requestAnimationFrame
合并多次属性修改,减少渲染次数 - 局部更新控制:通过
@Track
标记需要观察的属性 - 惰性加载:对深层嵌套数据动态加载@Observed类,降低内存占用
- 精准监听:通过
@Track
标记关键属性,避免全量更新(如仅监听user.name
) - 避免混用装饰器:@Observed类不可与其他类装饰器(如@Serializable)共用
二、使用方法
1. @Provide与@Consume
// 祖先组件提供共享主题配置
@Component
struct ThemeProvider {
@Provide themeConfig = {
primaryColor: '#2196F3',
fontSize: 16
}
}
// 深层子组件消费配置
@Component
struct ButtonComponent {
@Consume themeConfig: ThemeConfig
build() {
Button('提交')
.backgroundColor(this.themeConfig.primaryColor)
.fontSize(this.themeConfig.fontSize)
}
}
- 动态覆盖:通过
allowOverride: true
允许子组件重写祖先组件的@Provide状态,实现局部覆盖全局配置
@Component
struct Parent {
@Provide({ allowOverride: true }) config = { theme: 'light' }
}
@Component
struct Child {
@Provide config = { theme: 'dark' } // 覆盖父级配置
}
- 使用Map存储共享状态时需配合@Observed装饰器
2. @Observed与@ObjectLink
@Observed
class Product {
constructor(public name: string, public stock: number) {}
}
// 父组件管理商品列表
@Component
struct ProductList {
@State products: Product[] = [
new Product('手机', 100),
new Product('平板', 50)
]
build() {
Column() {
ForEach(this.products, (item) => {
ProductItem({ product: item })
})
}
}
}
// 子组件修改库存
@Component
struct ProductItem {
@ObjectLink product: Product
build() {
Row() {
Text(`${this.product.name} 库存: ${this.product.stock}`)
Button('-').onClick(() => this.product.stock--)
}
}
}
实现要点:
- 必须使用
new
创建@Observed类实例 - 数组操作需保持引用变更(如
this.products = [...this.products]
)
三、应用场景
1. @Provide/@Consume适用场景
- 全局主题管理:跨层级组件共享UI主题配置
- 用户登录状态:在任意子组件访问用户凭证
- 多步骤表单:跨表单页共享填写数据
2. @Observed/@ObjectLink适用场景
- 购物车系统:实时同步商品数量变更
- 树形结构编辑:嵌套对象属性修改(如组织架构)
- 表格数据操作:二维数组单元格内容更新
四、进阶技巧
1. 联合类型与复杂结构
@Provide user: User | null = null // 支持联合类型
@Consume('user') currentUser: User | null
@Observed
class Order {
items: Map<number, CartItem> = new Map() // 支持Map类型
}
优化策略:
- 对大型Map使用
ObjectLink
时优先修改引用 - 使用
readonly
修饰符减少代理开销
五、对比分析与选型指南
装饰器组合 | 核心功能 | 适用场景 | 优缺点对比 |
---|---|---|---|
@State + @Link | 父子组件单向/双向同步 | 简单父子状态传递 | 无法处理嵌套结构 |
@Provide + @Consume | 跨层级组件双向同步 | 复杂组件树通信 | 需要严格命名规范 |
@Observed + @ObjectLink | 嵌套对象/数组观察 | 二维数组、嵌套类属性监听 | 需要配合自定义类使用 |
关键差异:
- 作用范围:@Provide/@Consume作用于组件树层级,@Observed/@ObjectLink专注于数据结构深度。
- 同步机制:前者依赖组件生命周期绑定,后者通过Proxy代理实现实时观测。
六、避坑指南
- 命名规范建议
- 使用
snake_case
命名@Provide变量(如user_profile
) - 避免全局变量污染,优先使用别名隔离
- 使用
- 性能优化:
- 对大型数组/Map使用
@Observed
时,优先修改引用而非直接操作元素。 - 使用
readonly
修饰不可变数据,减少不必要的代理开销。
- 对大型数组/Map使用
- 常见错误处理
- 未使用@Observed装饰类
class User { ... } @ObjectLink user: User // 运行时错误 // 正确: @Observed class User { ... }
- 确保@Consume变量在祖先组件中存在同名@Provide声明,避免运行时错误。
七、总结
- @Provide/@Consume 是跨层级通信的"高速公路",适合全局状态共享(如主题切换、用户登录)
- @Observed/@ObjectLink 是深度数据观察的"显微镜",专治嵌套结构更新难题(如订单详情、树形菜单)