RM新时代网站-首页

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Harmony 鴻蒙頁面級變量的狀態(tài)管理

王程 ? 來源:jf_75796907 ? 作者:jf_75796907 ? 2024-01-25 10:42 ? 次閱讀

頁面級變量的狀態(tài)管理

@State、@Prop、@Link、@Provide、@Consume、@ObjectLink、@Observed和@Watch用于管理頁面級變量的狀態(tài)。

@State

@State裝飾的變量是組件內(nèi)部的狀態(tài)數(shù)據(jù),當(dāng)這些狀態(tài)數(shù)據(jù)被修改時,將會調(diào)用所在組件的build方法進行UI刷新。
@State狀態(tài)數(shù)據(jù)具有以下特征:

  • 支持多種類型數(shù)據(jù):支持class、number、boolean、string強類型數(shù)據(jù)的值類型和引用類型,以及這些強類型構(gòu)成的數(shù)組,即Array、Array、Array、Array。不支持object和any。
  • 支持多實例:組件不同實例的內(nèi)部狀態(tài)數(shù)據(jù)獨立。
  • 內(nèi)部私有:標記為@State的屬性是私有變量,只能在組件內(nèi)訪問。
  • 需要本地初始化:必須為所有@State變量分配初始值,變量未初始化可能導(dǎo)致未定義的框架異常行為。
  • 創(chuàng)建自定義組件時支持通過狀態(tài)變量名設(shè)置初始值:在創(chuàng)建組件實例時,可以通過變量名顯式指定@State狀態(tài)變量的初始值。

示例:

在下面的示例中:

  • 用戶定義的組件MyComponent定義了@State狀態(tài)變量count和title。如果count或title的值發(fā)生變化,則執(zhí)行MyComponent的build方法來重新渲染組件;
  • EntryComponent中有多個MyComponent組件實例,第一個MyComponent內(nèi)部狀態(tài)的更改不會影響第二個MyComponent;
  • 創(chuàng)建MyComponent實例時通過變量名給組件內(nèi)的變量進行初始化,如:
MyComponent({ title: { value: 'Hello World 2' }, count: 7 })
// xxx.ets
class Model {
  value: string

  constructor(value: string) {
    this.value = value
  }
}

@Entry
@Component
struct EntryComponent {
  build() {
    Column() {
      MyComponent({ count: 1, increaseBy: 2 }) // 第1個MyComponent實例
      MyComponent({ title: { value: 'Hello World 2' }, count: 7 }) // 第2個MyComponent實例
    }
  }
}

@Component
struct MyComponent {
  @State title: Model = { value: 'Hello World' }
  @State count: number = 0
  private toggle: string = 'Hello World'
  private increaseBy: number = 1

  build() {
    Column() {
      Text(`${this.title.value}`).fontSize(30)
      Button('Click to change title')
        .margin(20)
        .onClick(() = > {
          // 修改內(nèi)部狀態(tài)變量title
          this.title.value = (this.toggle == this.title.value) ? 'Hello World' : 'Hello ArkUI'
        })

      Button(`Click to increase count=${this.count}`)
        .margin(20)
        .onClick(() = > {
          // 修改內(nèi)部狀態(tài)變量count
          this.count += this.increaseBy
        })
    }
  }

@Prop

@Prop與@State有相同的語義,但初始化方式不同。@Prop裝飾的變量必須使用其父組件提供的@State變量進行初始化,允許組件內(nèi)部修改@Prop變量,但變量的更改不會通知給父組件,父組件變量的更改會同步到@prop裝飾的變量,即@Prop屬于單向數(shù)據(jù)綁定。
@Prop狀態(tài)數(shù)據(jù)具有以下特征:

  • 支持簡單類型:僅支持number、string、boolean等簡單數(shù)據(jù)類型;
  • 私有:僅支持組件內(nèi)訪問;
  • 支持多個實例:一個組件中可以定義多個標有@Prop的屬性;
  • 創(chuàng)建自定義組件時將值傳遞給@Prop變量進行初始化:在創(chuàng)建組件的新實例時,必須初始化所有@Prop變量,不支持在組件內(nèi)部進行初始化。

說明:
@Prop修飾的變量不能在組件內(nèi)部進行初始化。

示例:

在下面的示例中,當(dāng)按“+1”或“-1”按鈕時,父組件狀態(tài)發(fā)生變化,重新執(zhí)行build方法,此時將創(chuàng)建一個新的CountDownComponent組件實例。父組件的countDownStartValue狀態(tài)變量被用于初始化子組件的@Prop變量,當(dāng)按下子組件的“count - costOfOneAttempt”按鈕時,其@Prop變量count將被更改,CountDownComponent重新渲染,但是count值的更改不會影響父組件的countDownStartValue值。

// xxx.ets
@Entry
@Component
struct ParentComponent {
  @State countDownStartValue: number = 10 // 初始化countDownStartValue

  build() {
    Column() {
      Text(`Grant ${this.countDownStartValue} nuggets to play.`).fontSize(18)
      Button('+1 - Nuggets in New Game')
        .margin(15)
        .onClick(() = > {
          this.countDownStartValue += 1
        })

      Button('-1  - Nuggets in New Game')
        .margin(15)
        .onClick(() = > {
          this.countDownStartValue -= 1
        })
      // 創(chuàng)建子組件時,必須在構(gòu)造函數(shù)參數(shù)中提供其@Prop變量count的初始值,同時初始化常規(guī)變量costOfOneAttempt(非Prop變量)
      CountDownComponent({ count: this.countDownStartValue, costOfOneAttempt: 2 })
    }
  }
}

@Component
struct CountDownComponent {
  @Prop count: number
  private costOfOneAttempt: number

  build() {
    Column() {
      if (this.count > 0) {
        Text(`You have ${this.count} Nuggets left`).fontSize(18)
      } else {
        Text('Game over!').fontSize(18)
      }

      Button('count - costOfOneAttempt')
        .margin(15)
        .onClick(() = > {
          this.count -= this.costOfOneAttempt
        })
    }
  }
}

@Link

@Link裝飾的變量可以和父組件的@State變量建立雙向數(shù)據(jù)綁定:

  • 支持多種類型:@Link支持的數(shù)據(jù)類型與@State相同,即class、number、string、boolean或這些類型的數(shù)組;
  • 私有:僅支持組件內(nèi)訪問;
  • 單個數(shù)據(jù)源:父組件中用于初始化子組件@Link變量的必須是父組件定義的狀態(tài)變量;
  • 雙向通信:子組件對@Link變量的更改將同步修改父組件中的@State變量;
  • 創(chuàng)建自定義組件時需要將變量的引用傳遞給@Link變量,在創(chuàng)建組件的新實例時,必須使用命名參數(shù)初始化所有@Link變量。@Link變量可以使用@State變量或@Link變量的引用進行初始化,@State變量可以通過'$'操作符創(chuàng)建引用。

說明:
@Link修飾的變量不能在組件內(nèi)部進行初始化。

簡單類型示例:

@Link語義是從''操作符引出,即isPlaying是this.isPlaying內(nèi)部狀態(tài)的雙向數(shù)據(jù)綁定。當(dāng)單擊子組件PlayButton中的按鈕時,@Link變量更改,PlayButton與父組件中的Text和Button將同時進行刷新,同樣地,當(dāng)點擊父組件中的Button修改this.isPlaying時,子組件PlayButton與父組件中的Text和Button也將同時刷新。

// xxx.ets
@Entry
@Component
struct Player {
  @State isPlaying: boolean = false

  build() {
    Column() {
      PlayButton({ buttonPlaying: $isPlaying })
      Text(`Player is ${this.isPlaying ? '' : 'not'} playing`).fontSize(18)
      Button('Parent:' + this.isPlaying)
        .margin(15)
        .onClick(() = > {
          this.isPlaying = !this.isPlaying
        })
    }
  }
}

@Component
struct PlayButton {
  @Link buttonPlaying: boolean

  build() {
    Column() {
      Button(this.buttonPlaying ? 'pause' : 'play')
        .margin(20)
        .onClick(() = > {
          this.buttonPlaying = !this.buttonPlaying
        })
    }
  }
}

復(fù)雜類型示例:

// xxx.ets
@Entry
@Component
struct Parent {
  @State arr: number[] = [1, 2, 3]

  build() {
    Column() {
      Child({ items: $arr })
      Button('Parent Button: splice')
        .margin(10)
        .onClick(() = > {
          this.arr.splice(0, 1, 60)
        })
      ForEach(this.arr, item = > {
        Text(item.toString()).fontSize(18).margin(10)
      }, item = > item.toString())
    }
  }
}


@Component
struct Child {
  @Link items: number[]

  build() {
    Column() {
      Button('Child Button1: push')
        .margin(15)
        .onClick(() = > {
          this.items.push(100)
        })
      Button('Child Button2: replace whole item')
        .margin(15)
        .onClick(() = > {
          this.items = [100, 200, 300]
        })
    }
  }
}

@Link、@State和@Prop結(jié)合使用示例:
下面示例中,ParentView包含ChildA和ChildB兩個子組件,ParentView的狀態(tài)變量counter分別用于初始化ChildA的@Prop變量和ChildB的@Link變量。

  • ChildB使用@Link建立雙向數(shù)據(jù)綁定,當(dāng)ChildB修改counterRef狀態(tài)變量值時,該更改將同步到ParentView和ChildA共享;
  • ChildA使用@Prop建立從ParentView到自身的單向數(shù)據(jù)綁定,當(dāng)ChildA修改counterVal狀態(tài)變量值時,ChildA將重新渲染,但該更改不會傳達給ParentView和ChildB。
// xxx.ets
@Entry
@Component
struct ParentView {
  @State counter: number = 0

  build() {
    Column() {
      ChildA({ counterVal: this.counter })
      ChildB({ counterRef: $counter })
    }
  }
}

@Component
struct ChildA {
  @Prop counterVal: number

  build() {
    Button(`ChildA: (${this.counterVal}) + 1`)
      .margin(15)
      .onClick(() = > {
        this.counterVal += 1
      })
  }
}

@Component
struct ChildB {
  @Link counterRef: number

  build() {
    Button(`ChildB: (${this.counterRef}) + 1`)
      .margin(15)
      .onClick(() = > {
        this.counterRef += 1
      })
  }
}

@Observed和ObjectLink數(shù)據(jù)管理

當(dāng)開發(fā)者需要在子組件中針對父組件的一個變量(parent_a)設(shè)置雙向同步時,開發(fā)者可以在父組件中使用@State裝飾變量(parent_a),并在子組件中使用@Link裝飾對應(yīng)的變量(child_a)。這樣不僅可以實現(xiàn)父組件與單個子組件之間的數(shù)據(jù)同步,也可以實現(xiàn)父組件與多個子組件之間的數(shù)據(jù)同步。如下圖所示,可以看到,父子組件針對ClassA類型的變量設(shè)置了雙向同步,那么當(dāng)子組件1中變量對應(yīng)的屬性c的值變化時,會通知父組件同步變化,而當(dāng)父組件中屬性c的值變化時,會通知所有子組件同步變化。

然而,上述例子是針對某個數(shù)據(jù)對象進行的整體同步,而當(dāng)開發(fā)者只想針對父組件中某個數(shù)據(jù)對象的部分信息進行同步時,使用@Link就不能滿足要求。如果這些部分信息是一個類對象,就可以使用@ObjectLink配合@Observed來實現(xiàn),如下圖所示。

設(shè)置要求

@Observed用于類,@ObjectLink用于變量。

@ObjectLink裝飾的變量類型必須為類(class type)。

  • 類要被@Observed裝飾器所裝飾。
  • 不支持簡單類型參數(shù),可以使用@Prop進行單向同步。

@ObjectLink裝飾的變量是不可變的。

  • 屬性的改動是被允許的,當(dāng)改動發(fā)生時,如果同一個對象被多個@ObjectLink變量所引用,那么所有擁有這些變量的自定義組件都會被通知進行重新渲染。

@ObjectLink裝飾的變量不可設(shè)置默認值。

  • 必須讓父組件中有一個由@State、@Link、@StorageLink、@Provide或@Consume裝飾的變量所參與的TS表達式進行初始化。

@ObjectLink裝飾的變量是私有變量,只能在組件內(nèi)訪問。

示例

// xxx.ets
// 父組件ViewB中的類對象ClassA與子組件ViewA保持數(shù)據(jù)同步時,可以使用@ObjectLink和@Observed,綁定該數(shù)據(jù)對象的父組件和其他子組件同步更新
var nextID: number = 0

@Observed
class ClassA {
  public name: string
  public c: number
  public id: number

  constructor(c: number, name: string = 'OK') {
    this.name = name
    this.c = c
    this.id = nextID++
  }
}

@Component
struct ViewA {
  label: string = 'ViewA1'
  @ObjectLink a: ClassA

  build() {
    Row() {
      Button(`ViewA [${this.label}] this.a.c= ${this.a.c} +1`)
        .onClick(() = > {
          this.a.c += 1
        })
    }.margin({ top: 10 })
  }
}

@Entry
@Component
struct ViewB {
  @State arrA: ClassA[] = [new ClassA(0), new ClassA(0)]

  build() {
    Column() {
      ForEach(this.arrA, (item) = > {
        ViewA({ label: `#${item.id}`, a: item })
      }, (item) = > item.id.toString())
      ViewA({ label: `this.arrA[first]`, a: this.arrA[0] })
      ViewA({ label: `this.arrA[last]`, a: this.arrA[this.arrA.length - 1] })

      Button(`ViewB: reset array`)
        .margin({ top: 10 })
        .onClick(() = > {
          this.arrA = [new ClassA(0), new ClassA(0)]
        })
      Button(`ViewB: push`)
        .margin({ top: 10 })
        .onClick(() = > {
          this.arrA.push(new ClassA(0))
        })
      Button(`ViewB: shift`)
        .margin({ top: 10 })
        .onClick(() = > {
          this.arrA.shift()
        })
    }.width('100%')
  }
}

@Provide和@Consume

@Provide作為數(shù)據(jù)的提供方,可以更新其子孫節(jié)點的數(shù)據(jù),并觸發(fā)頁面渲染。@Consume在感知到@Provide數(shù)據(jù)的更新后,會觸發(fā)當(dāng)前自定義組件的重新渲染。

說明:
使用@Provide和@Consume時應(yīng)避免循環(huán)引用導(dǎo)致死循環(huán)。

@Provide

名稱說明
裝飾器參數(shù)是一個string類型的常量,用于給裝飾的變量起別名。如果規(guī)定別名,則提供對應(yīng)別名的數(shù)據(jù)更新。如果沒有,則使用變量名作為別名。推薦使用@Provide(‘a(chǎn)lias’)這種形式。
同步機制@Provide的變量類似@State,可以修改對應(yīng)變量進行頁面重新渲染。也可以修改@Consume裝飾的變量,反向修改@State變量。
初始值必須設(shè)置初始值。
頁面重渲染場景基礎(chǔ)類型(boolean,string,number)變量的改變;@Observed class類型變量及其屬性的修改;添加,刪除,更新數(shù)組中的元素。

@Consume

類型說明
初始值不可設(shè)置默認初始值。

示例

// xxx.ets
@Entry
@Component
struct CompA {
  @Provide("reviewVote") reviewVotes: number = 0;

  build() {
    Column() {
      CompB()
      Button(`CompA: ${this.reviewVotes}`)
        .margin(10)
        .onClick(() = > {
          this.reviewVotes += 1;
        })
    }
  }
}

@Component
struct CompB {
  build() {
    Column() {
      CompC()
    }
  }
}

@Component
struct CompC {
  @Consume("reviewVote") reviewVotes: number

  build() {
    Column() {
      Button(`CompC: ${this.reviewVotes}`)
        .margin(10)
        .onClick(() = > {
          this.reviewVotes += 1
        })
    }.width('100%')
  }
}

@Watch

@Watch用于監(jiān)聽狀態(tài)變量的變化,語法結(jié)構(gòu)為:

@State @Watch("onChanged") count : number = 0

如上所示,給狀態(tài)變量增加一個@Watch裝飾器,通過@Watch注冊一個回調(diào)方法onChanged, 當(dāng)狀態(tài)變量count被改變時, 觸發(fā)onChanged回調(diào)。

裝飾器@State、@Prop、@Link、@ObjectLink、@Provide、@Consume、@StorageProp以及@StorageLink所裝飾的變量均可以通過@Watch監(jiān)聽其變化。

說明:
深層次數(shù)據(jù)修改不會觸發(fā)@Watch回調(diào),例如無法監(jiān)聽數(shù)組中對象值的改變。

// xxx.ets
@Entry
@Component
struct CompA {
  @State @Watch('onBasketUpdated') shopBasket: Array< number > = [7, 12, 47, 3]
  @State totalPurchase: number = 0
  @State addPurchase: number = 0

  aboutToAppear() {
    this.updateTotal()
  }

  updateTotal(): void {
    let sum = 0;
    this.shopBasket.forEach((i) = > {
      sum += i
    })
    // 計算新的購物籃總價值,如果超過100,則適用折扣
    this.totalPurchase = (sum < 100) ? sum : 0.9 * sum
    return this.totalPurchase
  }

  // shopBasket更改時觸發(fā)該方法
  onBasketUpdated(propName: string): void {
    this.updateTotal()
  }

  build() {
    Column() {
      Button('add to basket ' + this.addPurchase)
        .margin(15)
        .onClick(() = > {
          this.addPurchase = Math.round(100 * Math.random())
          this.shopBasket.push(this.addPurchase)
        })
      Text(`${this.totalPurchase}`)
        .fontSize(30)
    }
  }
}

審核編輯 黃宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 變量
    +關(guān)注

    關(guān)注

    0

    文章

    613

    瀏覽量

    28360
  • 鴻蒙
    +關(guān)注

    關(guān)注

    57

    文章

    2339

    瀏覽量

    42805
  • Harmony
    +關(guān)注

    關(guān)注

    0

    文章

    52

    瀏覽量

    2601
收藏 人收藏

    評論

    相關(guān)推薦

    OpenHarmony頁面變量狀態(tài)管理

    @State、@Prop、@Link、@Provide、Consume、@ObjectLink、@Observed和@Watch用于管理頁面變量
    的頭像 發(fā)表于 12-07 08:58 ?2671次閱讀

    鴻蒙開發(fā)教程-管理組件狀態(tài)

    跨組件層級雙向同步狀態(tài)是指@Provide修飾的狀態(tài)變量自動對提供者組件的所有后代組件可用,后代組件通過使用@Consume裝飾的變量來獲得對提供的狀態(tài)變量的訪問。
    的頭像 發(fā)表于 01-22 21:46 ?1315次閱讀
    <b class='flag-5'>鴻蒙</b>開發(fā)教程-<b class='flag-5'>管理</b>組件<b class='flag-5'>狀態(tài)</b>

    鴻蒙OS開發(fā)實例:【應(yīng)用狀態(tài)變量共享】

    平時在開發(fā)的過程中,我們會在應(yīng)用中共享數(shù)據(jù),在不同的頁面間共享信息。雖然常用的共享信息,也可以通過不同頁面中組件間信息共享的方式,但有時使用應(yīng)用級別的狀態(tài)管理會讓開發(fā)工作變得簡單。
    的頭像 發(fā)表于 04-03 15:09 ?1318次閱讀
    <b class='flag-5'>鴻蒙</b>OS開發(fā)實例:【應(yīng)用<b class='flag-5'>狀態(tài)變量</b>共享】

    harmonyos開發(fā)者平臺

    使用@State、@Prop、@Link、@Watch、@Provide、@Consume管理頁面變量狀態(tài),實現(xiàn)對
    的頭像 發(fā)表于 05-06 16:02 ?837次閱讀
    harmonyos開發(fā)者平臺

    HarmonyOS實踐之應(yīng)用狀態(tài)變量共享

    平時在開發(fā)的過程中,我們會在應(yīng)用中共享數(shù)據(jù),在不同的頁面間共享信息。雖然常用的共享信息,也可以通過不同頁面中組件間信息共享的方式,但有時使用應(yīng)用級別的狀態(tài)管理會讓開發(fā)工作變得簡單。 根
    發(fā)表于 12-27 09:48

    Harmony 鴻蒙頁面變量狀態(tài)管理

    頁面變量狀態(tài)管理 @State、@Prop、@Link、@Provide、@Consume、@ObjectLink、@Observed和
    發(fā)表于 01-24 20:04

    Harmony如何管理網(wǎng)頁

    你好,我可能錯過了一些東西,但我找不到關(guān)于Harmony如何管理網(wǎng)頁(使用文件系統(tǒng))以生成HTTP服務(wù)器中使用的c代碼的答案。我使用BSP和相關(guān)的評估板(PIC32MZ EC)啟動了一個項目。當(dāng)生成
    發(fā)表于 07-30 12:25

    如何設(shè)置鴻蒙harmony控件的高度是屏幕的一半?

    鴻蒙harmony想讓控件的高度是屏幕的一半,該如何設(shè)置?在XML和java代碼中,分別該如何設(shè)置?
    發(fā)表于 03-23 11:09

    OpenHarmony應(yīng)用ArkUI 狀態(tài)管理開發(fā)范例

    稱為狀態(tài)管理機制。 自定義組件擁有變量,變量必須被裝飾器裝飾才可以成為狀態(tài)變量狀態(tài)變量的改變會
    發(fā)表于 09-01 15:03

    OpenHarmony頁面UI狀態(tài)存儲:LocalStorage

    LocalStorage是頁面的UI狀態(tài)存儲,通過@Entry裝飾器接收的參數(shù)可以在頁面內(nèi)共享同一個LocalStorage實例。LocalStorage也可以在UIAbility內(nèi)
    發(fā)表于 10-17 17:01

    鴻蒙 OS 應(yīng)用開發(fā)初體驗

    kotlin 語言了,編程語言變成了類 JavaScript 的前端語言,這意味著我們需要適應(yīng)用前端的思想去開發(fā)鴻蒙應(yīng)用,比如狀態(tài)管理。 總結(jié) 本文純初體驗遙遙領(lǐng)先背后的鴻蒙操作系統(tǒng)
    發(fā)表于 11-02 19:38

    狀態(tài)變量濾波器,狀態(tài)變量濾波器原理是什么?

    狀態(tài)變量濾波器,狀態(tài)變量濾波器原理是什么? 狀態(tài)變量濾波器,又稱多態(tài)變量濾波器,它可以分別從不同的點同時輸出高通、帶通、低通等,且
    發(fā)表于 03-24 14:24 ?6629次閱讀

    華為p40升鴻蒙系統(tǒng)步驟教程

    華為鴻蒙(英語:Harmony OS,開發(fā)代號:Ark)是基于微內(nèi)核的全場景分布式OS。
    的頭像 發(fā)表于 07-06 11:24 ?5974次閱讀

    Harmony 鴻蒙應(yīng)用變量狀態(tài)管理

    應(yīng)用變量狀態(tài)管理 在前面的章節(jié)中,已經(jīng)講述了如何管理頁面
    的頭像 發(fā)表于 01-24 21:30 ?493次閱讀
    <b class='flag-5'>Harmony</b> <b class='flag-5'>鴻蒙</b>應(yīng)用<b class='flag-5'>級</b><b class='flag-5'>變量</b>的<b class='flag-5'>狀態(tài)</b><b class='flag-5'>管理</b>

    鴻蒙開發(fā):【頁面棧及任務(wù)鏈】

    單個UIAbility組件可以實現(xiàn)多個頁面,并在多個頁面之間跳轉(zhuǎn),這種UIAbility組件內(nèi)部的頁面跳轉(zhuǎn)關(guān)系稱為“頁面?!?,由ArkUI框架統(tǒng)一
    的頭像 發(fā)表于 06-14 10:10 ?407次閱讀
    <b class='flag-5'>鴻蒙</b>開發(fā):【<b class='flag-5'>頁面</b>棧及任務(wù)鏈】
    RM新时代网站-首页