前文,关于@Param的使用: HarmonyOS-ArkUIV2装饰器-@Param:组件外部输入-CSDN博客
@Once装饰器是一个需要配合@Param装饰器一块使用的的装饰器。它的特性是,仅仅在变量进行初始化的时候,接受一个外部传来的值进行初始化,然后就不接受后续同步变化了, 当后续数据源进行更改时,不会将修改同步给子组件。它的底层是针对数据源的变化做了拦截操作。
使用方式
我们知道@Param要和@Once一块配合使用,会使得数据只更新一次,但是Param本身而言,它就不能被重新赋值。如果我们拿简单类型的变量来用Param测试,是根本测不到的。因此我们拿@Local作为数据源进行测试。这个可以改动!
@Entry
@ComponentV2
struct ParamTest {
@Local name: string = "name" //注意这里用的是Local修饰,因为我们的测试场景涉及到被重新赋值
build() {
Column() {
Button("change Name")
.onClick(() => {
// 随机数计算X Y
let num1 = Math.floor(Math.random() * 100) + 1;
let num2 = Math.floor(Math.random() * 100) + 1;
this.name = `${num1}-${num2}`
})
Text(`once值在父组件为:${this.name}`) //这块会正常更新
// 子组件
Child({ //在调用子组件的时候就传入其初始化的变量. @Param支持组件间传递数据
name: this.name
})
.margin({ top: 20 })
.backgroundColor(Color.Orange)
}
.height('100%')
.width('100%')
}
}
@ComponentV2
struct Child {
@Param @Once @Require name: string
build() {
Column() {
Text(`name: ${this.name}`) //这块只更新了一次后续数据源再更新它也不更新了
}
}
}
注意事项
-
@Once只能和@Param一块使用,如上方代码那样使用
-
@Once是V2版本的装饰器,所以您的组件装饰器一定要是@ComponentV2。
-
@Once的修饰只是表明在组件信息传递上只会接受第一次初始化,而后源数据的变动不再同步。但是并没有说明自己这个值就不能改了。相反,@Param修饰的变量不能被改动,正好被这个@Once装饰器给破掉这个规则了。经由它修饰的变量反而能自己改值。但是改的这个值不会向上层同步给父组件。只是在其组件区域内进行更新。
@Entry
@ComponentV2
struct ParamTest {
@Local name: string = "name" //注意这里用的是Local修饰,因为我们的测试场景涉及到被重新赋值build() { Column() { Button("change Name") .onClick(() => { // 随机数计算X Y let num1 = Math.floor(Math.random() * 100) + 1; let num2 = Math.floor(Math.random() * 100) + 1; this.name = `${num1}-${num2}` }) Text(`once值在父组件为:${this.name}`) //这块会正常更新 // 子组件 Child({ //在调用子组件的时候就传入其初始化的变量. @Param支持组件间传递数据 name: this.name }) .margin({ top: 20 }) .backgroundColor(Color.Orange) } .height('100%') .width('100%') }
}
@ComponentV2
struct Child {
@Param @Once @Require name: string
build() {
Column() {
Text(name: ${this.name}
) //这块只更新了一次后续数据源再更新它也不更新了
}
.onClick(() => {
this.name = "aaaaa" //可以改值
})
}
}
}
当修饰复杂数据时
修饰复杂类型跟修饰简单类型是两码事哈。只要Once不把这个引用断掉重新赋值, 那么父组件修改了复杂类型的某一个小属性,once是可以更新界面的。但是如果是once修改了属性,其父控件是不更新的。有兴趣的可以试一下下方代码感受:
@ObservedV2 //使此类对象具备可观测能力
class Region {
@Trace x: number //被观测的变量, 这个类型是基本类型
@Trace y: number //被观测的变量
constructor(x: number, y: number) {
this.x = x
this.y = y
}
}
class Info {
region: Region
constructor(x: number, y: number) {
this.region = new Region(x, y)
}
}
@Entry
@ComponentV2
struct ParamTest {
@Param info: Info = new Info(0, 0)
@Param onceInfoTest: Info = new Info(1, 1)
@Local name: string = "name"
build() {
Column() {
Button("Change Region x = " + this.info.region.x)
.onClick(() => {
// 随机数计算X Y
let num1 = Math.floor(Math.random() * 100) + 1;
let num2 = Math.floor(Math.random() * 100) + 1;
this.info.region.x = num1
this.info.region.y = num2
this.onceInfoTest.region.x += 1 //这个值变化,是用来测试once修饰的复杂类型,是否感知到其中一个属性的变化,从而更新界面。测试结果是可以更新
this.onceInfoTest.region.y += 1
this.name = `${num1}-${num2}` //点击的时候Name不断变化,在测试简单类型在子组件是否还更新。这里的操作是重新赋值。
})
Text(`父组件 name=:${this.name}`)
.margin({top: 20})
// 子组件
Child({ //在调用子组件的时候就传入其初始化的变量. @Param支持组件间传递数据
regionObjectLink: this.info.region,
regionProp: this.info.region,
infoProp: this.info,
infoLink: this.info,
info: this.onceInfoTest,
name: this.name
})
.margin({ top: 20 })
.backgroundColor(Color.Orange)
}
.height('100%')
.width('100%')
}
}
@ComponentV2
struct Child {
@Param @Require regionObjectLink: Region // 如果Param修饰的变量不进行初始化,预编译就会出问题
@Param @Require regionProp: Region
@Param @Require infoProp: Info
@Param @Require infoLink: Info
@Param @Once @Require info: Info
@Param @Once @Require name: string
build() {
Column() {
Text(`ObjectLink region: ${this.regionObjectLink.x}-${this.regionObjectLink.y}`)
Text(`Prop regionProp: ${this.regionProp.x}-${this.regionProp.y}`)
Text(`Prop infoProp: ${this.infoProp.region.x}-${this.infoProp.region.y}`)
Text(`Link infoLink: ${this.infoLink.region.x}-${this.infoLink.region.y}`)
Text(`Once info: ${this.info.region.x}-${this.info.region.y}`)
Text(`once 子组件 name: ${this.name}`)
}
.onClick(() => {
this.info.region.x = 200 //这值即使改成200,父组件中界面不更新
this.info.region.y = 200
this.name = "aaaaa" //这块代表once修饰的属性,是可以破了param的规则,从而变成可以被重新赋值的。
})
}
}