【高心星出品】
V2装饰器@Monitor的使用
概念
为了增强状态管理框架对状态变量变化的监听能力,开发者可以使用@Monitor装饰器对状态变量进行监听。
@Monitor装饰器用于监听状态变量修改,使得状态变量具有深度监听的能力:
- @Monitor装饰器支持在@ComponentV2装饰的自定义组件中使用,未被状态变量装饰器@Local、@Param、@Provider、@Consumer、@Computed装饰的变量无法被@Monitor监听到变化。
- @Monitor装饰器支持在类中与@ObservedV2、@Trace配合使用,不允许在未被@ObservedV2装饰的类中使用@Monitor装饰器。未被@Trace装饰的属性无法被@Monitor监听到变化。
- 当观测的属性变化时,@Monitor装饰器定义的回调方法将被调用。判断属性是否变化使用的是严格相等(===),当严格相等判断的结果是false(即不相等)的情况下,就会触发@Monitor的回调。当在一次事件中多次改变同一个属性时,将会使用初始值和最终值进行比较以判断是否变化。
- 单个@Monitor装饰器能够同时监听多个属性的变化,当这些属性在一次事件中共同变化时,只会触发一次@Monitor的回调方法。
- @Monitor装饰器具有深度监听的能力,能够监听嵌套类、多维数组、对象数组中指定项的变化。对于嵌套类、对象数组中成员属性变化的监听要求该类被@ObservedV2装饰且该属性被@Trace装饰。
- 当@Monitor监听整个数组时,更改数组的某一项不会被监听到。无法监听内置类型(Array、Map、Date、Set)的API调用引起的变化。
- 在继承类场景中,可以在父子组件中对同一个属性分别定义@Monitor进行监听,当属性变化时,父子组件中定义的@Monitor回调均会被调用。
- 和@Watch装饰器类似,开发者需要自己定义回调函数,区别在于@Watch装饰器将函数名作为参数,而@Monitor直接装饰回调函数。@Monitor与@Watch的对比可以查看@Monitor与@Watch的对比。
特性 | @Monitor | @Watch |
---|---|---|
监听深度 | 支持深层属性监听 | 仅第一层属性监听 |
多属性监听 | 支持同时监听多个属性 | 仅支持单个属性监听 |
新旧值获取 | 提供变化前后的完整值 | 无法获取旧值 |
回调触发机制 | 多属性同事件变化合并触发 | 每个属性变化独立触发 |
类属性监听 | 需配合@ObservedV2和@Trace | 不支持类属性监听 |
-
深度监听能力
- 支持监听嵌套类、多维数组、对象数组中指定项的变化(需配合@ObservedV2和@Trace使用)
- 示例场景:监听用户信息对象中
address.city
属性的变化,仅当该属性修改时触发回调
-
多属性联合监听
可同时监听多个属性变化,当多个属性在一次事件中共同变化时,仅触发一次回调:@Monitor('age', 'height') onRecordChange(monitor: IMonitor) { console.log(`年龄或身高变化:${this.age}, ${this.height}`); }
-
变化值追踪
提供变化前后的值对比能力,通过monitor.value()?.before
和monitor.value()?.now
访问新旧值
Imonitor接口说明
在鸿蒙ArkTS状态管理V2中,IMonitor
是配合 @Monitor
装饰器使用的核心接口,用于捕获和响应状态变量的细粒度变化。以下是关键特性和用法解析:
一、IMonitor 核心功能
-
变化路径追踪
通过monitor.dirty
获取所有触发回调的变量路径集合,适用于同时监听多个路径的场景:@Monitor('users.0.name', 'users.1.age') onChanges(monitor: IMonitor) { monitor.dirty.forEach((path: string) => { console.log(`路径 ${path} 发生变化`); }); }
-
值变化对比
monitor.value(path)
返回一个包含before
和now
属性的对象,用于获取变化前后的值:const change = monitor.value('dimensionTwo.0.0'); console.log(`旧值:${change?.before} → 新值:${change?.now}`);
-
路径动态解析
支持多维数组和嵌套对象路径(如array.0.property
),要求路径必须与被@Trace
装饰的属性严格匹配。
案例
monitor的基本使用:
下面案例monitor观察两个基本变量,当两个变量改变的时候,显示旧数据和新数据。
@Entry
@ComponentV2
struct Monitorpage {
// 被观察的数据必须由@local @param或者@provider装饰
@Local name: string = 'gxx'
@Local age: number = 30
@Local msg:string=''
// 观察的变量要一一列举
@Monitor('name','age')
// Imonitor 中的dirty代表改变的变量名字,类型为数组
// Imonitor 中的value代表该dirty对应的数据,包含旧数据和新数据
ondatachange(data: IMonitor) {
data.dirty.forEach((item: string) => {
let before = data.value(item)?.before!
let after = data.value(item)?.now!
this.msg+=`${item} 旧数据:${before} 新数据:${after}\n`
})
}
build() {
Column({ space: 20 }) {
Button('page name: ' + this.name)
.width('60%')
.onClick(() => {
// 同时改变name和age
this.name = 'ggl'
this.age += 1
})
Button('page age: ' + this.age)
.width('60%')
.onClick(() => {
this.age += 1
})
Text(this.msg).fontSize(20)
}
.height('100%')
.width('100%')
}
}
monitor观察复杂对象:
下面案例monitor观察的数据是复杂对象,需要使用@observed才能观察到属性的变化。
@ObservedV2
class user{
@Trace name:string
@Trace age:number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
}
@Entry
@ComponentV2
struct Monitorpage {
// 被观察的数据必须由@local @param或者@provider装饰
@Local u: user = new user('gxx',30)
@Local msg:string=''
// 观察对象U 属性改变不会引起刷新
@Monitor('u.name','u.age')
// Imonitor 中的dirty代表改变的变量名字,类型为数组
// Imonitor 中的value代表该dirty对应的数据,包含旧数据和新数据
ondatachange(data: IMonitor) {
data.dirty.forEach((item: string) => {
let before = data.value(item)?.before!
let after = data.value(item)?.now!
this.msg+=`${item} 旧数据:${before} 新数据:${after}\n`
})
}
build() {
Column({ space: 20 }) {
Button('page name: ' + this.u.name)
.width('60%')
.onClick(() => {
// 同时改变name和age
this.u.name = 'ggl'
this.u.age += 1
})
Button('page age: ' + this.u.age)
.width('60%')
.onClick(() => {
this.u.age += 1
})
Text(this.msg).fontSize(20)
}
.height('100%')
.width('100%')
}
}
monitor观察数组元素和数组长度案例:
下面这个案例monitor将要观察的是数组元素和数组长度的变化。
@Entry
@ComponentV2
struct Monitorpage {
// 被观察的数据必须由@local @param或者@provider装饰
@Local nums:number[]=[1,2,3]
@Local msg:string=''
// 观察数组nums的元素nums.i和数组长度 nums.length
@Monitor('nums.0','nums.length')
// Imonitor 中的dirty代表改变的变量名字,类型为数组
// Imonitor 中的value代表该dirty对应的数据,包含旧数据和新数据
ondatachange(data: IMonitor) {
data.dirty.forEach((item: string) => {
let before = data.value(item)?.before!
let after = data.value(item)?.now!
this.msg+=`${item} 旧数据:${before} 新数据:${after}\n`
})
}
build() {
Column({ space: 20 }) {
Button('page nums.0: ' + this.nums[0])
.width('60%')
.onClick(() => {
// 新增元素
this.nums[0]=this.nums[0]+1
this.nums.push(this.nums[this.nums.length-1]+1)
})
Button('page nums.1: ' + this.nums[1])
.width('60%')
.onClick(() => {
// 更新数据
this.nums[1]=10
})
Text(this.msg).fontSize(20)
}
.height('100%')
.width('100%')
}
}