状态管理V2 鸿蒙开发更好用状态管理的装饰器?

前言:

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%')
  }
}
相关推荐
moxiaoran57531 小时前
uni-app萌宠案例学习笔记--页面布局和CSS样式设置
前端·css·uni-app
CrissChan2 小时前
Pycharm 函数注释
java·前端·pycharm
小小小小宇3 小时前
Vue.nextTick()笔记
前端
Georgewu3 小时前
【HarmonyOS 5】鸿蒙中Stage模型与FA模型详解
harmonyos
小约翰仓鼠4 小时前
vue3子组件获取并修改父组件的值
前端·javascript·vue.js
Lin Hsüeh-ch'in4 小时前
Vue 学习路线图(从零到实战)
前端·vue.js·学习
烛阴4 小时前
bignumber.js深度解析:驾驭任意精度计算的终极武器
前端·javascript·后端
计蒙不吃鱼4 小时前
一篇文章实现Android图片拼接并保存至相册
android·java·前端
全职计算机毕业设计5 小时前
基于Java Web的校园失物招领平台设计与实现
java·开发语言·前端