鸿蒙开发:V2版本装饰器之@Monitor装饰器
前言
本文代码案例基于Api13。
随着官方的迭代,在新的Api中,对于新的应用开发,官方已经建议直接使用V2所属的装饰器进行开发了,所以,能上手V2的尽量上手V2吧,毕竟,V2是V1的增强版本,为开发者提供更多功能和灵活性,由V1升成V2,肯定是大势所趋;但是,毕竟V1有着大量的应用基础,使用的也非常广泛,如果V1版本的功能和性能已能满足需求,其实也不用切换,总之就一句话:新的应用尽量使用V2,老的应用,如果V1满足可以不切换V2,如果功能受限,建议循序渐进的进行切换。
本篇文章主要概述下V2版本装饰器中的@Monitor装饰器,它对标的是V1中的@Watch装饰器,但是使用上和功能上均有所不同。
记得之前在写刷新组件的时候,有一个功能,需要监听当前刷新或加载的关闭状态,然后去执行关闭动画等逻辑,使用的就是@Watch装饰器,简单的逻辑如下:
class RefreshController {closeRefresh: boolean = falsecloseLoadMore: boolean = false
}@Entry
@Component
struct Index {@State @Watch("listenerController") refreshController: RefreshController = new RefreshController()listenerController() {console.log("==当前刷新状态:" + this.refreshController.closeRefresh)console.log("==当前加载状态:" + this.refreshController.closeLoadMore)}build() {Column() {Button("关闭刷新").onClick(() => {this.refreshController.closeRefresh = true})Button("关闭加载").margin({ top: 20 }).onClick(() => {this.refreshController.closeLoadMore = true})}.height('100%').width('100%').justifyContent(FlexAlign.Center)}
}
运行后,我们点击按钮进行打印日志:
虽然执行的状态,我们通过@Watch装饰器监听到了,也能实现我们的逻辑,但是存在一个问题,本身我们只想监听到某一个状态的改变,比如只监听刷新状态,或者只监听加载状态,但是@Watch装饰器是,你无论监听哪一个,都统统给你返回,显然会影响我们做针对性的逻辑判断。
除此之外,还存在一个问题,变量更改前的值是什么,在这里无法获取,在业务逻辑复杂的场景下,我们是无法准确知道是哪一个属性或元素发生了改变从而触发了@Watch事件,这非常不便于我们对变量的更改进行准确监听。
针对以上的弊端,V2版本中的@Monitor装饰器,则弥补了这一缺陷,实现对对象、数组中某一单个属性或数组项变化的监听,并且能够获取到变化之前的值。
更改为@Monitor装饰器后,针对属性单独监听。
@ObservedV2
class RefreshController {@Trace closeRefresh: boolean = false@Trace closeLoadMore: boolean = false
}@Entry
@ComponentV2
struct Index {@Local refreshController: RefreshController = new RefreshController()@Monitor("refreshController.closeRefresh")closeRefreshChange() {console.log("==当前刷新状态:" + this.refreshController.closeRefresh)}@Monitor("refreshController.closeLoadMore")closeLoadMoreChange() {console.log("==当前加载状态:" + this.refreshController.closeLoadMore)}build() {Column() {Button("关闭刷新").onClick(() => {this.refreshController.closeRefresh = true})Button("关闭加载").margin({ top: 20 }).onClick(() => {this.refreshController.closeLoadMore = true})}.height('100%').width('100%').justifyContent(FlexAlign.Center)}
}
使用方式
首先需要注意,@Monitor监听的变量,一定是被@Local、@Param、@Provider、@Consumer、@Computed装饰,否则是无法监听的,这一点,务必须知。
第一步,使用@Local、@Param、@Provider、@Consumer、@Computed等其中之一,修饰你的变量,例如下代码:
@Local testContent: string = "测试数据一"
第二步,使用@Monitor装饰器进行监听,方法名自己定义,要求,@Monitor("变量名"),其中变量名一定要和第一步的变量名字保持一致,例如下代码:
@Monitor("testContent")testContentChange() {console.log("==属性testContent发生了改变:" + this.testContent)}
动态改变属性testContent,就会触发@Monitor装饰器修饰的函数;@Monitor装饰器支持监听多个状态变量,直接逗号分隔即可,多个属性中,任意一个属性发生了改变都会进行回调。
@Monitor("testContent","testNumber")
testChange() {console.log("==testContent属性:" + this.testContent + ",testNumber属性:" + this.testNumber)}
获取改变之前的值
如果你想拿到当前属性改变之前的值,那么就需要在函数中传递IMonitor类型的参数。
IMonitor类型的变量用作@Monitor装饰方法的参数。
属性 | 类型 | 参数 | 返回值 | 说明 |
dirty | Array<string> | 无 | 无 | 保存发生变化的属性名。 |
value<T> | function | path?: string | IMonitorValue<T> | 获得指定属性(path)的变化信息。当不填path时返回@Monitor监听顺序中第一个改变的属性的变化信息。 |
MonitorValue<T>类型
IMonitorValue<T>类型保存了属性变化的信息,包括属性名、变化前值、当前值。
属性 | 类型 | 说明 |
before | T | 监听属性变化之前的值。 |
now | T | 监听属性变化之后的当前值。 |
path | string | 监听的属性名。 |
@Monitor("testContent")testChange(monitor: IMonitor) {monitor.dirty.forEach((path: string) => {console.log("==属性值改变之前:" + monitor.value(path)?.before + ",属性值改变之后:" + monitor.value(path)?.now)})}
如果只想监听改变之后的值,IMonitor参数可以省略。
对象监听
在前言中,我们可以看到,监听对象中的属性变化时,需要使用@Trace装饰,如果未被装饰,则是无法进行监听的,所以在实际的开发中,如果需要针对对象的单一属性进行监听时,@Trace装饰务必使用。
如果不装饰,那么就需要重新创建对象,虽然这种方式也能正常的监听到,但是并不是唯一属性的监听,在实际的开发中是不推荐的。
以下案例未使用@Trace装饰,不建议使用。
class RefreshController {closeRefresh: boolean = falsecloseLoadMore: boolean = false
}@Entry
@ComponentV2
struct Index {@Local refreshController: RefreshController = new RefreshController()@Monitor("refreshController.closeRefresh")closeRefreshChange() {console.log("==当前刷新状态:" + this.refreshController.closeRefresh)}@Monitor("refreshController.closeLoadMore")closeLoadMoreChange() {console.log("==当前加载状态:" + this.refreshController.closeLoadMore)}build() {Column() {Button("关闭刷新").onClick(() => {this.refreshController = new RefreshController()this.refreshController.closeRefresh = true})Button("关闭加载").margin({ top: 20 }).onClick(() => {this.refreshController = new RefreshController()this.refreshController.closeLoadMore = true})}.height('100%').width('100%').justifyContent(FlexAlign.Center)}
}
如果你想监听整个对象的属性变化,@Trace装饰可以不使用。
class RefreshController {closeRefresh: boolean = falsecloseLoadMore: boolean = false
}@Entry
@ComponentV2
struct Index {@Local refreshController: RefreshController = new RefreshController()@Monitor("refreshController")statusChange() {console.log("==当前刷新状态:" + this.refreshController.closeRefresh)console.log("==当前加载状态:" + this.refreshController.closeLoadMore)}build() {Column() {Button("关闭刷新").onClick(() => {this.refreshController = new RefreshController()this.refreshController.closeRefresh = true})Button("关闭加载").margin({ top: 20 }).onClick(() => {this.refreshController = new RefreshController()this.refreshController.closeLoadMore = true})}.height('100%').width('100%').justifyContent(FlexAlign.Center)}
}
除了在UI组件可以进行监听,在自身对象中也是可以进行监听的,方便在对象中做一些逻辑,同样也支持在继承类场景下,同一个属性进行多次监听。
@ObservedV2
class RefreshController {@Trace closeRefresh: boolean = false@Trace closeLoadMore: boolean = false@Monitor("closeRefresh")closeRefreshChange() {console.log("==监听对象中的当前刷新状态:" + this.closeRefresh)}
}
通用监听能力
@Monitor装饰器,除了正常的数据监听之外,还支持对数组中的元素进行监听,包括多维数组,对象数组,虽然可以正常监听其值的变化,但是无法监听内置类型(Array、Map、Date、Set)的API调用引起的变化。
还有一点需要注意,当@Monitor监听数组整体变化时,只能通过监听数组的长度变化来判断数组是否有插入、删除等变化,以下是一个简单的二位数组数据改变案例:
@Entry
@ComponentV2
struct Index {@Local numberArray: number[][] = [[1, 1, 1], [2, 2, 2], [3, 3, 3]];@Monitor("numberArray.0.0", "numberArray.1.1")statusChange() {console.log("==数据改变:" + this.numberArray)}build() {Column() {Button("改变").onClick(() => {this.numberArray[0][0]++})}.height('100%').width('100%').justifyContent(FlexAlign.Center)}
}
可以发现以上的案例,只要数据发生了变化,就会执行数据监听方法。
监听对象整体改变时,如果当前属性未发生改变时,则不会触发@Monitor回调。
如下案例,依次点击按钮,会发生,按钮一不会执行任何方法,因为属性的值一样,未发生变化,按钮二和按钮三则可以正常执行。
@ObservedV2
class RefreshController {@Trace closeRefresh: boolean = false@Trace closeLoadMore: boolean = falseconstructor(closeRefresh: boolean, closeLoadMore: boolean) {this.closeRefresh = closeRefresh;this.closeLoadMore = closeLoadMore;}
}@Entry
@ComponentV2
struct Index {@Local refreshController: RefreshController = new RefreshController(false, false)@Monitor("refreshController.closeRefresh")closeRefreshChange() {console.log("==当前刷新状态:" + this.refreshController.closeRefresh)}@Monitor("refreshController.closeLoadMore")closeLoadMoreChange() {console.log("==当前加载状态:" + this.refreshController.closeLoadMore)}build() {Column() {Button("不会走").onClick(() => {this.refreshController = new RefreshController(false, false)})Button("关闭刷新").margin({ top: 20 }).onClick(() => {this.refreshController = new RefreshController(true, false)})Button("关闭加载").margin({ top: 20 }).onClick(() => {this.refreshController = new RefreshController(false, true)})}.height('100%').width('100%').justifyContent(FlexAlign.Center)}
}
相关总结
如果要实现@Monitor监听,其变量一定要被@Local、@Param、@Provider、@Consumer、@Computed装饰,未被修饰则无法被监听,还有,如果监听对象的变化,则不建议在一个类中对同一个属性进行多次@Monitor的监听,多次监听,只有最后一个定义的监听方法才会有效。
相关文章:

鸿蒙开发:V2版本装饰器之@Monitor装饰器
前言 本文代码案例基于Api13。 随着官方的迭代,在新的Api中,对于新的应用开发,官方已经建议直接使用V2所属的装饰器进行开发了,所以,能上手V2的尽量上手V2吧,毕竟,V2是V1的增强版本,…...

51单片机-外部中断
以外部中断0为例: 主程序中需要有以下代码: EA1; //打开总中断开关 EX01; //开外部中断0 IT00/1; 设置外部中断的触发方式 P3.2\P3.3为外部中断接口,通过控制P3.2口按键按下实现LED灯反转点亮 #include "reg52.h"typed…...

UE C++ UObject 功能的初步总结(结合官方文档)
一. Uboject的官方文档的个人理解 Objects in Unreal Engine | 虚幻引擎 5.5 文档 | Epic Developer Community 目录在此 1.垃圾回收:上篇文章简单介绍过,UObject的创建和回收。本身是很复杂的功能,后续会接着单项深入分析。 2.引用更新 1. 反射:之前…...
DeepSeek和ChatGPT的全面对比
一、模型基础架构对比(2023技术版本) 维度DeepSeekChatGPT模型家族LLAMA架构改进GPT-4优化版本参数量级开放7B/35B/120B闭源175B位置编码RoPE NTK扩展ALiBiAttention机制FlashAttention-3FlashAttention-2激活函数SwiGLU ProGeGLU训练框架DeepSpeedMeg…...
Spring Boot Actuator 监控✨
Spring Boot Actuator 是 Spring Boot 提供的一个强大的监控和管理工具,它可以帮助你深入了解和监控你的应用程序的运行状态。通过 Actuator,你可以获取应用程序的健康状况、内存使用情况、线程信息、HTTP 请求跟踪等。🚀 核心知识点 &#…...
构建高效 Python Web 应用:框架与服务器的选择及实践
构建高效 Python Web 应用:框架与服务器的选择及实践 flyfish 从选择合适的 Web 框架(如 Flask 和 FastAPI)到部署时选用适当的 Web 服务器(如 Waitress、Gunicorn、uWSGI 和 Uvicorn)的全过程。它不仅介绍了各个框架…...

LED灯闪烁实验:Simulink应用层开发
文章目录 1 阶段目标2 开发过程2.1 模型搭建2.2 模型仿真2.3 数据字典配置2.4 代码生成3 总结1 阶段目标 本文是《LED灯闪烁实验》的第三部分,会通过图文结合的方式,手把手带读者操作Simulink工具进行LED灯闪烁的应用层开发。 本章的开发可分解为如下若干过程: 方波输出建…...

在做题中学习(89):螺旋矩阵
解法:模拟 思路:创建ret数组,用变量标记原矩阵的行数和列数,遍历一个元素就push_back进ret数组,每次遍历完一行或一列,相应行/列数--,进行顺时针螺旋遍历到为0即可。 细节:要有边界…...
使用EasyExcel和多线程实现高效数据导出
使用EasyExcel和多线程实现高效数据导出 1. 概述 在企业级应用中,数据导出是一个常见的需求。为了提高导出效率,尤其是在处理大量数据时,我们可以结合使用EasyExcel库和多线程技术。本文将详细介绍如何通过EasyExcel和多线程技术实现高…...

rabbitmq五种模式的实现——springboot
rabbitmq五种模式的实现——springboot 基础知识和javase的实现形式可以看我之前的博客 代码地址:https://github.com/9lucifer/rabbitmq4j-learning 一、进行集成 (一)Spring Boot 集成 RabbitMQ 概述 Spring Boot 提供了对 RabbitMQ 的自…...
每日学习Java之一万个为什么
9.Class <?> class1 Myclass.class 为什么要有通配符?传给谁用的? 首先,这里的class特指某个对象在JVM中的元数据集合。 有普通、接口、数组、基本类型、 void 类型、局部类、匿名类、枚举、注解 1.类型安全:通配符允许…...
寒假学习总结
整个寒假都走在数据结构与算法的路上,深入学习了其中多个板块,刷了一些与之对应的题目,下面来一期总结(c) (emmm,主播在寒假试着去学习了几大语言的语法基础(丢丢) 如Ja…...

Java Web开发实战与项目——用户认证与授权模块开发
Web应用中,用户认证与授权是至关重要的功能,确保只有合法用户才能访问受保护的资源。Spring Security作为一个强大的安全框架,支持多种认证与授权方式。在本章节中,我们将深入探讨三种常见的用户认证与授权方案:基于To…...

力扣每日一题【算法学习day.129】
前言 ###我做这类文章一个重要的目的还是记录自己的学习过程,我的解析也不会做的非常详细,只会提供思路和一些关键点,力扣上的大佬们的题解质量是非常非常高滴!!! 习题 1.数组列表中的最大距离 题目链接…...

uni-app发起网络请求的三种方式
uni.request(OBJECT) 发起网络请求 具体参数可查看官方文档uni-app data:请求的参数; header:设置请求的 header,header 中不能设置 Referer; method:请求方法; timeout:超时时间,单位 ms&a…...

字节火山云DeepSeek接入教程,支持联网,速度超快。
大家好,我是苍何。 在使用 DeepSeek 官网,实在是卡的我差点学猪叫,于是我一直在寻找替代方案。 要求就 2:满血,速度快。(当然能联网更好)。 我也一度使用了如硅基流动 API,发现也开…...
C语言指针学习笔记
1. 指针的定义 指针(Pointer)是存储变量地址的变量。在C语言中,指针是一种非常重要的数据类型,通过指针可以直接访问和操作内存。 2. 指针的声明与初始化 2.1 指针声明 指针变量的声明格式为:数据类型 *指针变量名…...
FreeRTOS-rust 编译分析
目录介绍 FreeRTOS-rust ├── .cargo # 对 cargo 本身的配置 │ └── config.toml ├── Cargo.toml # 对当前工作空间的配置 ├── freertos-cargo-build # 负责对 freertos 源码进行编译 │ ├── Cargo.toml # 对当前 package 进行配置 │ └…...

【解决方法】vite-plugin-svg-icons使用中出现问题[vite] Cannot find package ‘fast-glob‘
问题长这样: 参考文章:https://medium.com/wumeng9028/vite-plugin-svg-icons-error-cannot-find-package-fast-glob-8cb03d19c0ac 解决方法:pnpm add fast-glob -D package.json {"vite-plugin-svg-icons": "2.0.1"…...

[Qt] 使用QUndoStack运行到cmd->isObsolete()崩溃
redo/undo中又push了 崩溃情况崩溃原因解决方法 崩溃情况 在正常调用QUndoStack的redo/undo时,崩溃在了这里 unknown:0 QWidget: Cannot create a QWidget without QApplication. 崩溃原因 在正常调用QUndoStack的redo/undo时,因为自身的逻辑处理&a…...

聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...

Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...

Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...

基于Java+VUE+MariaDB实现(Web)仿小米商城
仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意:运行前…...
OD 算法题 B卷【正整数到Excel编号之间的转换】
文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的:a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...
0x-3-Oracle 23 ai-sqlcl 25.1 集成安装-配置和优化
是不是受够了安装了oracle database之后sqlplus的简陋,无法删除无法上下翻页的苦恼。 可以安装readline和rlwrap插件的话,配置.bahs_profile后也能解决上下翻页这些,但是很多生产环境无法安装rpm包。 oracle提供了sqlcl免费许可,…...

spring Security对RBAC及其ABAC的支持使用
RBAC (基于角色的访问控制) RBAC (Role-Based Access Control) 是 Spring Security 中最常用的权限模型,它将权限分配给角色,再将角色分配给用户。 RBAC 核心实现 1. 数据库设计 users roles permissions ------- ------…...