V2所属装饰器
1. @ObservedV2装饰器和@Trace装饰器:类属性变化观测
1.1 基本语法
@ObservedV2类装饰器:装饰class。需要放在class的定义前,使用new创建类对象。
@Trace成员变量装饰器:class中成员属性。属性的类型可以为number、string、boolean、class、Array、Date、Map、Set等类型。
@ObservedV2
class 类名 {
@Trace 属性1:类型
@Trace 属性2:类型
属性3:类型
}
注意事项
非@Trace装饰的成员属性用在UI上无法触发UI刷新- @Trace不能用在没有被@ObservedV2装饰的class上
- @Trace是class中属性的装饰器,不能用在struct中
- @ObservedV2、@Trace不能与@Observed、@Track混合使用。
- 使用@ObservedV2与@Trace装饰的类不能和@State等V1的装饰器混合使
- 继承自@ObservedV2的类无法和@State等V1的装饰器混用
- @ObservedV2的类实例
目前不支持使用JSON.stringify进行序列化
1.2 基本使用
如下所示
1.3 详细使用
请参考@ObservedV2装饰器和@Trace装饰器---使用场景
参考代码
@ObservedV2
class Person {
id: number
@Trace name: string
@Trace age: number
constructor(id: number, name: string, age: number) {
this.id = id
this.name = name
this.age = age
}
}
@Entry
@ComponentV2
struct Index {
person: Person = new Person(1, '张三', 18)
build() {
Column({ space: 20 }) {
Text('@ObservedV2-@Trace')
.fontSize(25)
Column(){
Row(){
Text('id'+this.person.id)
Button('修改数据')
.onClick(()=>{
this.person.id++
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
Row(){
Text('name'+this.person.name)
Button('修改数据')
.onClick(()=>{
this.person.name = '李四'
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
Row(){
Text('age'+this.person.age)
Button('修改数据')
.onClick(()=>{
this.person.age++
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
}
.width('100%')
.backgroundColor(Color.Pink)
}
.backgroundColor('#cbe69b')
.width('100%')
.height('100%')
.padding(20)
}
}
2. @ComponentV2装饰器:自定义组件
1.1 基本语法
@ComponentV2和@Component装饰器一样,@ComponentV2装饰器用于装饰自定义组件:
@ComponentV2
struct Index {
build() {
}
}
注意事项
- 在@ComponentV2装饰的自定义组件中,开发者仅可以使用全新的状态变量装饰器,包括@Local、@Param、@Once、@Event、@Provider、@Consumer等。
- @ComponentV2装饰的自定义组件暂不支持组件复用、LocalStorage等现有自定义组件的能力。
- 无法同时使用@ComponentV2与@Component装饰同一个struct结构。
- @ComponentV2支持一个可选的boolean类型参数freezeWhenInactive,来实现组件冻结功能。
3. @Local装饰器:组件内部状态
3.1 基本使用
除了无法从外部修改值,其他用法于与@State无异
@Entry
@ComponentV2
struct Index {
@Local count: number = 0;
@Local message: string = "Hello";
@Local flag: boolean = false;
build() {
Column() {
Text(`${this.count}`)
Text(`${this.message}`)
Text(`${this.flag}`)
Button("change Local")
.onClick(()=>{
this.count++;
this.message += " World";
this.flag = !this.flag;
})
}
}
}
注意事项
- @Local装饰器只能在@ComponentV2装饰的自定义组件中使用。
- @Local装饰的变量表示组件内部状态,
不允许从外部传入初始化
- 当装饰的变量类型是内置类型时,可以观察到变量整体赋值以及通过API调用带来的变化。
| 类型 | 可观测变化的API |
|---|---|
| Array | push、pop、shift、unshift、splice、copyWithin、fill、reverse、sort |
| Date | setFullYear, setMonth, setDate, setHours, setMinutes, setSeconds, setMilliseconds, setTime, setUTCFullYear, setUTCMonth, setUTCDate, setUTCHours, setUTCMinutes, setUTCSeconds, setUTCMilliseconds |
| Map | set, clear, delete |
| Set | add, clear, delete |
3.2 @Local与@State对比
| @State | @Local | |
|---|---|---|
| 参数 | 无。 | 无。 |
| 从父组件初始化 | 可选。 | 不允许外部初始化。 |
| 观察能力 | 能观测变量本身以及一层的成员属性,无法深度观测。 | 能观测变量本身,深度观测依赖@Trace装饰器。 |
| 数据传递 | 可以作为数据源和子组件中状态变量同步。 | 可以作为数据源和子组件中状态变量同步。 |
3.3 详细使用
请参考@Local装饰器
4. @Param:组件外部输入
4.1 基本使用
@Param表示组件从外部传入的状态,使得父子组件之间的数据能够进行同步(单向传递)类似于@prop
@Entry
@ComponentV2
struct Index {
@Local msg: string = 'Hello World'
build() {
Column() {
Text('index==' + this.msg)
son({
msg: this.msg})
}
.width('100%')
.height('100%')
}
}
@ComponentV2
export struct son {
@Param msg: string = ''
build() {
Column() {
Text('son==' + this.msg)
.onClick(() => {
this.updata()
})
}
}
}
修改值需要配合@Event
4.2 详细使用
5. @Once:初始化同步一次
5.1 基本用法
@Once装饰器仅在变量初始化时接受外部传入值进行初始化,当后续数据源更改时,不会将修改同步给子组件
@Entry
@ComponentV2
struct Index {
@Local msg: string = 'Hello World'
build() {
Column() {
Text('index==' + this.msg)
son({
msg: this.msg, updata: () => {
this.msg = 'hello v2'
}
})
}
.width('100%')
.height('100%')
}
}
@ComponentV2
export struct son {
@Once @Param msg: string = ''
@Event updata: () => void = () => {
}
build() {
Column() {
Text('son==' + this.msg)
.onClick(() => {
this.updata()
})
}
}
}
6. @Event装饰器:组件输出
6.1 基本使用
类似于子传父时使用回调函数,只不过用Event修饰了
@Entry
@ComponentV2
struct Index {
@Local msg: string = 'Hello World'
build() {
Column() {
Text('index==' + this.msg)
son({
msg: this.msg, updata: () => {
this.msg = 'hello v2'
}
})
}
.width('100%')
.height('100%')
}
}
@ComponentV2
export struct son {
@Param msg: string = ''
@Event updata: () => void = () => {
}
build() {
Column() {
Text('son==' + this.msg)
.onClick(() => {
this.updata()
})
}
}
}
7. @Provider装饰器和@Consumer装饰器:跨组件层级双向同步
7.1 基本概念
@Provider,即数据提供方,其所有的子组件都可以通过@Consumer绑定相同的key来获取@Provider提供的数据。
@Consumer,即数据消费方,可以通过绑定同样的key获取其最近父节点的@Provider的数据,当查找不到@Provider的数据时,使用本地默认值。
@Provider和@Consumer装饰数据类型需要一致。
开发者在使用@Provider和@Consumer时要注意:
- @Provider和@Consumer强依赖自定义组件层级,@Consumer所在组件会由于其父组件的不同,而被初始化为不同的值。
- @Provider和@Consumer相当于把组件粘合在一起了,从组件独立角度,要减少使用@Provider和@Consumer。
7.2 基本使用
@Entry
@ComponentV2
struct Parent {
@Provider() str: string = 'hello';
build() {
Column() {
Button(this.str)
.onClick(() => {
this.str += '0';
})
Child()
}
}
}
@ComponentV2
struct Child {
@Consumer() str: string = 'world';
build() {
Column() {
Button(this.str)
.onClick(() => {
this.str += '0';
})
}
}
}
注意事项
key值不同无法建立联系
7.3 V2@Provider和@Consumer与V1@Provide和@Consume对比
| 能力 | V2装饰器@Provider和@Consumer | V1装饰器@Provide和@Consume |
|---|---|---|
| @Consume(r) | 允许本地初始化,当找不到@Provider的时候使用本地默认值。 | 禁止本地初始化,当找不到对应的的@Provide时候,会抛出异常。 |
| 支持类型 | 支持function。 | 不支持function。 |
| 观察能力 | 仅能观察自身赋值变化,如果要观察嵌套场景,配合@Trace一起使用。 | 观察第一层变化,如果要观察嵌套场景,配合@Observed和@ObjectLink一起使用。 |
| alias和属性名 | alias是唯一匹配的key,如果缺省alias,则默认属性名为alias。 | alias和属性名都为key,优先匹配alias,匹配不到可以匹配属性名。 |
| @Provide(r) 从父组件初始化 | 禁止。 | 允许。 |
| @Provide(r)支持重载 | 默认开启,即@Provider可以重名,@Consumer向上查找最近的@Provider。 | 默认关闭,即在组件树上不允许有同名@Provide。如果需要重载,则需要配置allowOverride。 |
8. @Monitor装饰器:状态变量修改监听
8.1 基本概念
@Monitor装饰器用于监听状态变量修改,使得状态变量具有深度监听的能力。
可以同时监听多个状态变量
可以监听多层
8.2 @Monitor装饰器与@Watch装饰器的区别
| @Watch | @Monitor | |
|---|---|---|
| 参数 | 回调方法名 | 监听状态变量名、属性名 |
| 监听目标数 | 只能监听单个状态变量 | 能同时监听多个状态变量 |
| 监听能力 | 跟随状态变量观察能力(一层) | 跟随状态变量观察能力(深层) |
| 能否获取变化前的值 | 不能获取变化前的值 | 能获取变化前的值 |
| 监听条件 | 监听对象为状态变量 | 监听对象为状态变量或为@Trace装饰的类成员属性 |
| 使用限制 | 仅能在@Component装饰的自定义组件中使用 | 能在@ComponentV2装饰的自定义组件中使用,也能在@ObservedV2装饰的类中使用 |
8.3 基本使用
@ObservedV2
class Person {
id: number
@Trace name: string
@Trace age: number
constructor(id: number, name: string, age: number) {
this.id = id
this.name = name
this.age = age
}
@Monitor('id')
onIdChange(monitor: IMonitor) {
console.log(`id change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
}
@Monitor('name')
onNameChange(monitor: IMonitor) {
console.log(`name change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
}
@Monitor('age')
onAgeChange(monitor: IMonitor) {
console.log(`age change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
}
}
@Entry
@ComponentV2
struct Index {
@Local message: string = "Hello World";
@Local name: string = "Tom";
@Local age: number = 24;
person: Person = new Person(1, '张三', 18)
@Monitor("message", "name")
onStrChange(monitor: IMonitor) {
monitor.dirty.forEach((path: string) => {
console.log(`${path} changed from ${monitor.value(path)?.before} to ${monitor.value(path)?.now}`)
})
}
build() {
Column() {
Text(this.message)
Text(this.name)
Text(this.age.toString())
Button("改变单层")
.onClick(() => {
this.message += "!";
this.name = "Jack";
})
Divider()
Column() {
Text(this.person.id.toString())
Text(this.person.name)
Text(this.person.age.toString())
Button("改变对象")
.onClick(() => {
this.person.id += 1;
this.person.name = "李四";
this.person.age += 1;
})
}
}
}
}
尤其注意监听对象时监听器的位置,应该写在类中
改变前
改变后
虽然对象的id属性也设置了监听,但是没有用@Trace修饰,故监听不到变化