前言:
ArkTs 采用的是MVVM 的数据架构,为了更加趋近MVVM 模式,状态管理装饰器由先前的V1
推进到了V2
宏观局别
V1
强调组件层级的状态管理,而V2
则增强了对数据对象的深度观察与管理能力,不再局限于组件层级。通过V2
,开发者能够更灵活地控制数据和状态,实现更高效的UI
刷新。
V2新增装饰器
@ComponentV2
当我们需要使用V2
装饰器时,需要提前声明这个组件是V2
版本的组件,否则无法使用V2
中所包含的所有装饰器。
js
@ComponentV2 // 装饰器
struct Index { // struct声明的数据结构
build() { // build定义的UI
}
}
@Local
在V1
当中装饰组件内部的状态变量采用的是@State
,在V2
当中的@Local
与其功能类似但并不完全相同。
对于简单的数据类型,两者并没有任何区别
但对于复杂数据类型,两者有本质的区别
js
interface iPerson{
name:string;
age:number;
}
@Entry
@ComponentV2
struct Index {
@Local num: number = 0;
@Local person:iPerson={name:'张三',age:30}
build() {
Column(){
Text('num:'+this.num)
Text(this.person.name+':'+this.person.age)
Divider()
Button('++')
.onClick(() => {
this.num++;
this.person.age++
})
Button('覆盖')
.onClick(() => {
this.person={name:'李四',age:40}
})
}
.width('100%')
}
}
如上图所示,@Local
并不能和@State
一样监听到第一层的数据,但是对于直接覆盖整个对象的操作确实和@State
一样能够被监听到。
@ObservedV2 & @Trace
@ObservedV2
和@Trace
提供了对嵌套类对象属性变化直接观测的能力,是状态管理V2
中相对核心的能力之一。
与V1
中@Observed
一样都只能对class
声明的对象类型使用。
通常配合@Trace
一起使用,@Trace
装饰器装饰的属性property
变化时,仅会通知property
关联的组件进行刷新。
值得一提的是@ObservedV2
并不支持使用JSON.stringify
进行序列化。
js
@ObservedV2
class iPerson {
@Trace name: string;
@Trace age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
@Entry
@ComponentV2
struct Index {
@Local p1:iPerson = new iPerson('张三', 18)
build() {
Column() {
Text('name:' + this.p1.name)
Text(this.p1.name + ':' + this.p1.age)
Text(JSON.stringify(this.p1))
Button('++')
.onClick(() => {
this.p1.age++;
})
Divider()
}
.width('100%')
}
}
如上图所示,使用JSON
时,即使age
变化了,依然无法监听到。
@Param
这个装饰器主要是用来接受来自父组件传递的数据的,他并不具备修改数据的能力。
如果是对于基本数据而言只能对其进行读操作,不能改变基本数据类型的值编译器会直接报错。
但对于复杂的数据类型,可以配合上文提到的@Trace
使用,完成对复杂数据类型的修改
js
@ObservedV2
class iPerson {
@Trace name: string;
@Trace age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
@Entry
@ComponentV2
struct Index {
@Local num:number=0
@Local p1:iPerson = new iPerson('张三', 18)
build() {
Column() {
Text(this.p1.name + ':' + this.p1.age)
Text('num:'+this.num)
Divider()
Son({
num: this.num,
p1:this.p1
})
}
.width('100%')
}
}
@ComponentV2
struct Son {
@Param num: number = 0
@Param p1: iPerson = new iPerson('', 0)
build() {
Column(){
Button('++')
.onClick(() => {
this.p1.age++;
// this.num++;
})
}
}
}
@Once
@Once
必须 搭配@Param
使用,单独使用或搭配其他装饰器使用都是不允许的。
为了弥补在前文中提到的@Param
无法修改父组件传递的值,当@Once
搭配其使用时能使得@Param
具备了这个能力,但修改的状态不会影响父组件。
js
@ObservedV2
class iPerson {
@Trace name: string;
@Trace age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
@Entry
@ComponentV2
struct Index {
@Local num:number=0
@Local p1:iPerson = new iPerson('张三', 18)
build() {
Column() {
Text(this.p1.name + ':' + this.p1.age)
Text('num:'+this.num)
Divider()
Son({
num: this.num,
p1:this.p1
})
}
.width('100%')
}
}
@ComponentV2
struct Son {
@Once @Param num: number = 0
@Once @Param p1: iPerson = new iPerson('', 0)
build() {
Column(){
Text('子组件Once修饰的num:'+this.num)
Button('++')
.onClick(() => {
this.p1.age++;
this.num++;
})
}
}
}
由于是仅针对数据源的变化做拦截,对于复杂数据类型并不影响。
@Event
@Event
主要配合@Param
实现数据的双向同步
由于@Param
装饰的变量在本地无法更改,使用@Event
装饰器装饰回调方法并调用,可以实现更改数据源的变量,再通过@Local
的同步机制,将修改同步回@Param
,以此达到主动更新@Param
装饰变量的效果。
js
@ObservedV2
class iPerson {
@Trace name: string;
@Trace age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
@Entry
@ComponentV2
struct Index {
@Local num:number=0
@Local p1:iPerson = new iPerson('张三', 18)
// @Local num2:number=0
build() {
Column() {
Text(this.p1.name + ':' + this.p1.age)
Text('num:'+this.num)
Divider()
Son({
num: this.num,
p1:this.p1,
handler:(n:number)=>{
this.num+=n
this.p1.age+=n
}
})
}
.width('100%')
}
}
@ComponentV2
struct Son {
@Param num: number = 0
@Param p1: iPerson = new iPerson('', 0)
@Event handler:(num:number)=>void
build() {
Column(){
Text(this.p1.name + ':' + this.p1.age)
Text('num:'+this.num)
Button('+1')
.onClick(() => {
this.handler(1);
})
Button('+10')
.onClick(() => {
this.handler(10);
})
}
}
}
如上述代码所示,使用@event
从父组件中接受到方法handler
,将参数的逻辑写入到子组件中,通过父组件调用该方法,从而实现了父子组件的数据双向绑定。
@Monitor
@Monitor装饰器用于监听状态变量修改,使得状态变量具有深度监听的能力
@Monitor 能够收集所有被他监视的状态改变的属性,并将该属性记录下来。改变的属性均会被记录在dirty数组当中。
js
@ObservedV2
class iPerson {
@Trace name: string;
@Trace age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
@Entry
@ComponentV2
struct Index {
@Local num1:number=100
@Local num2:number=200
@Monitor("num1","num2")
handler(monitor:IMonitor){
console.log('-----monitor.value:',JSON.stringify(monitor.dirty))
monitor.dirty.forEach((item)=>{
console.log("-----",JSON.stringify(monitor.value(item)))
})
}
build() {
Column() {
Text('num1:'+this.num1)
Text('num2:'+this.num2)
Button('+1')
.onClick(() => {
this.num1++;
this.num2++;
})
}
.width('100%')
}
}
@Computed
@Computed为方法装饰器,装饰getter方法。@Computed会检测被计算的属性变化,当被计算的属性变化时,@Computed只会被求解一次
用法与vue3中的计算属性十分相似。同样也具有缓存能力
js
@Entry
@ComponentV2
struct Index {
@Local num1:number=100
@Local num2:number=200
@Computed get sum(){
return this.num1+this.num2
}
build() {
Column() {
Text('num1:'+this.num1)
Text('num2:'+this.num2)
Text('sum:'+this.sum)
Button('+1')
.onClick(() => {
this.num1++;
this.num2++;
})
}
.width('100%')
}
}