鴻蒙開發筆記-8-組件狀態管理裝飾器:@Provide/@Consume與@Observed/@ObjectLink

一、基本概念

1. @Provide與@Consume:跨組件樹雙向同步

這對裝飾器實現了任意層級組件間的雙向數據綁定。@Provide在祖先組件中聲明共享狀態,@Consume在后代組件中消費該狀態,形成類似"發布-訂閱"的機制。其突破傳統父子傳參的限制,支持組件樹中任意層級的通信。

  • 自動廣播機制:@Provide變量變更時,所有關聯@Consume組件自動更新
  • 別名匹配規則:支持通過相同變量名或別名建立綁定(如@Provide('count')與@Consume('count'))
  • 類型嚴格校驗:要求@Provide與@Consume變量類型完全一致
  • 支持ObjectClassArray等復雜類型

2. @Observed與@ObjectLink:深度數據觀察

這對裝飾器專為解決嵌套對象/數組的深度監聽問題而設計。傳統@State只能感知對象引用的變化,而@Observed通過代理機制實現對類屬性的細粒度觀察,@ObjectLink則建立與父組件@Observed實例的雙向綁定。

  • 支持多層嵌套屬性變更(如user.address.city)
  • 支持MapSet等復雜數據結構(需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代理實現實時觀測。

六、避坑指南

  1. 命名規范建議
    • 使用snake_case命名@Provide變量(如user_profile
    • 避免全局變量污染,優先使用別名隔離
  2. 性能優化:
    • 對大型數組/Map使用@Observed時,優先修改引用而非直接操作元素。
    • 使用readonly修飾不可變數據,減少不必要的代理開銷。
  3. 常見錯誤處理
    • 未使用@Observed裝飾類
    class User { ... }
    @ObjectLink user: User  // 運行時錯誤
    
    // 正確:
    @Observed class User { ... }
    
    • 確保@Consume變量在祖先組件中存在同名@Provide聲明,避免運行時錯誤。

七、總結

  • @Provide/@Consume 是跨層級通信的"高速公路",適合全局狀態共享(如主題切換、用戶登錄)
  • @Observed/@ObjectLink 是深度數據觀察的"顯微鏡",專治嵌套結構更新難題(如訂單詳情、樹形菜單)

我是今陽,如果想要進階和了解更多的干貨,歡迎關注wx gzh “今陽說” 接收我的最新文章

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容