赞
踩
@State、@Prop、@Link、@Provide、@Consume、@ObjectLink、@Observed和@Watch用于管理页面级变量的状态。
@State装饰的变量是组件内部的状态数据,当这些状态数据被修改时,将会调用所在组件的build方法进行UI刷新。
@State状态数据具有以下特征:
在下面的示例中:
用户定义的组件MyComponent定义了@State状态变量count和title。如果count或title的值发生变化,则执行MyComponent的build方法来重新渲染组件;
EntryComponent中有多个MyComponent组件实例,第一个MyComponent内部状态的更改不会影响第二个MyComponent;
创建MyComponent实例时通过变量名给组件内的变量进行初始化,如:
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(() => { // 修改内部状态变量title this.title.value = (this.toggle == this.title.value) ? 'Hello World' : 'Hello ArkUI' }) Button(`Click to increase count=${this.count}`) .margin(20) .onClick(() => { // 修改内部状态变量count this.count += this.increaseBy }) } }
@Prop与@State有相同的语义,但初始化方式不同。@Prop装饰的变量必须使用其父组件提供的@State变量进行初始化,允许组件内部修改@Prop变量,但变量的更改不会通知给父组件,父组件变量的更改会同步到@prop装饰的变量,即@Prop属于单向数据绑定。
@Prop状态数据具有以下特征:
说明:
@Prop修饰的变量不能在组件内部进行初始化。
在下面的示例中,当按“+1”或“-1”按钮时,父组件状态发生变化,重新执行build方法,此时将创建一个新的CountDownComponent组件实例。父组件的countDownStartValue状态变量被用于初始化子组件的@Prop变量,当按下子组件的“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 }) // 创建子组件时,必须在构造函数参数中提供其@Prop变量count的初始值,同时初始化常规变量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装饰的变量可以和父组件的@State变量建立双向数据绑定:
说明:
@Link修饰的变量不能在组件内部进行初始化。
@Link语义是从’ ′ 操作符引出,即 '操作符引出,即 ′操作符引出,即isPlaying是this.isPlaying内部状态的双向数据绑定。当单击子组件PlayButton中的按钮时,@Link变量更改,PlayButton与父组件中的Text和Button将同时进行刷新,同样地,当点击父组件中的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 }) } } }
// 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结合使用示例:
下面示例中,ParentView包含ChildA和ChildB两个子组件,ParentView的状态变量counter分别用于初始化ChildA的@Prop变量和ChildB的@Link变量。
// 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 }) } }
当开发者需要在子组件中针对父组件的一个变量(parent_a)设置双向同步时,开发者可以在父组件中使用@State装饰变量(parent_a),并在子组件中使用@Link装饰对应的变量(child_a)。这样不仅可以实现父组件与单个子组件之间的数据同步,也可以实现父组件与多个子组件之间的数据同步。如下图所示,可以看到,父子组件针对ClassA类型的变量设置了双向同步,那么当子组件1中变量对应的属性c的值变化时,会通知父组件同步变化,而当父组件中属性c的值变化时,会通知所有子组件同步变化。
然而,上述例子是针对某个数据对象进行的整体同步,而当开发者只想针对父组件中某个数据对象的部分信息进行同步时,使用@Link就不能满足要求。如果这些部分信息是一个类对象,就可以使用@ObjectLink配合@Observed来实现,如下图所示。
@Observed用于类,@ObjectLink用于变量。
@ObjectLink装饰的变量类型必须为类(class type)。
@ObjectLink装饰的变量是不可变的。
@ObjectLink装饰的变量不可设置默认值。
@ObjectLink装饰的变量是私有变量,只能在组件内访问。
// xxx.ets // 父组件ViewB中的类对象ClassA与子组件ViewA保持数据同步时,可以使用@ObjectLink和@Observed,绑定该数据对象的父组件和其他子组件同步更新 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数据的更新后,会触发当前自定义组件的重新渲染。
说明:
使用@Provide和@Consume时应避免循环引用导致死循环。
名称 | 说明 |
---|---|
装饰器参数 | 是一个string类型的常量,用于给装饰的变量起别名。如果规定别名,则提供对应别名的数据更新。如果没有,则使用变量名作为别名。推荐使用@Provide(‘alias’)这种形式。 |
同步机制 | @Provide的变量类似@State,可以修改对应变量进行页面重新渲染。也可以修改@Consume装饰的变量,反向修改@State变量。 |
初始值 | 必须设置初始值。 |
页面重渲染场景 | 基础类型(boolean,string,number)变量的改变;@Observed class类型变量及其属性的修改;添加,删除,更新数组中的元素。 |
类型 | 说明 |
---|---|
初始值 | 不可设置默认初始值。 |
// 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用于监听状态变量的变化,语法结构为:
@State @Watch("onChanged") count : number = 0
如上所示,给状态变量增加一个@Watch装饰器,通过@Watch注册一个回调方法onChanged, 当状态变量count被改变时, 触发onChanged回调。
装饰器@State、@Prop、@Link、@ObjectLink、@Provide、@Consume、@StorageProp以及@StorageLink所装饰的变量均可以通过@Watch监听其变化。
说明:
深层次数据修改不会触发@Watch回调,例如无法监听数组中对象值的改变。
// 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更改时触发该方法 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) } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。