鸿蒙开发:V2版本装饰器之@Monitor装饰器

程序员一鸣
• 阅读 25

前言

本文代码案例基于Api13。

随着官方的迭代,在新的Api中,对于新的应用开发,官方已经建议直接使用V2所属的装饰器进行开发了,所以,能上手V2的尽量上手V2吧,毕竟,V2是V1的增强版本,为开发者提供更多功能和灵活性,由V1升成V2,肯定是大势所趋;但是,毕竟V1有着大量的应用基础,使用的也非常广泛,如果V1版本的功能和性能已能满足需求,其实也不用切换,总之就一句话:新的应用尽量使用V2,老的应用,如果V1满足可以不切换V2,如果功能受限,建议循序渐进的进行切换。

本篇文章主要概述下V2版本装饰器中的@Monitor装饰器,它对标的是V1中的@Watch装饰器,但是使用上和功能上均有所不同。

记得之前在写刷新组件的时候,有一个功能,需要监听当前刷新或加载的关闭状态,然后去执行关闭动画等逻辑,使用的就是@Watch装饰器,简单的逻辑如下:

class RefreshController {
  closeRefresh: boolean = false
  closeLoadMore: 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)
  }
}

运行后,我们点击按钮进行打印日志:

鸿蒙开发:V2版本装饰器之@Monitor装饰器

虽然执行的状态,我们通过@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 = false
  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 = 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 = false
  closeLoadMore: 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)
  }
}
点赞
收藏
评论区
推荐文章
Python进阶者 Python进阶者
4年前
浅析装饰器的那些事儿
一、装饰器的简单定义外层函数返回里层函数的引用,里层函数引用外层函数的变量。二、装饰器的作用通俗来讲装饰器的作用就是在不改变已有函数代码前提下,为该函数增加新的功能。defrun():print('我会跑')fun()现在我想在原有函数的基础上新增一个功能:我会唱歌。这个时候利用装饰器则轻松可以帮我们实现这个功能。三、实
Stella981 Stella981
3年前
Google Maps Android API v2入门
概述创建一个新的使用了GoogleMapsAndroidAPIv2的Android应用需要许多个步骤。这一节中所概述的其中的许多步骤只是需要必须执行一次而已,但有些信息则是未来应用开发方便的参考。给一个Android应用程序添加一个地图的总体过程如下:1.安装AndroidSDK(https://www.osc
Wesley13 Wesley13
3年前
Oracle 中使用 fetch bulk collect into 批量效率的读取游标数据
通常我们获取游标数据是用fetchsome\_cursorintovar1,var2的形式,当游标中的记录数不多时不打紧。然而自Oracle8i起,Oracle为我们提供了fetchbulkcollect来批量取游标中的数据,存中即是合理的。它能在读取游标中大量数据的时候提升效率,就像SNMP协议中,V2版比V1版新加了
Stella981 Stella981
3年前
Django基础
http协议请求的格式(request)请求方式URLHTTP/1.1k1:v1k1:v2请求数据响应的格式(response)、HTTP/1.1状态描述k
Stella981 Stella981
3年前
Dubbo爆出严重漏洞!可导致网站被控制、数据泄露!附解决方案
http://dy.163.com/v2/article/detail/F5FPIFRU0511Q1AF.html  !(http://dingyue.ws.126.net/2020/0216/125ec4c4p00q5rcrs0019d200ig009qg00ig009q.png)  来源:华为云  原文地址:https://w
Wesley13 Wesley13
3年前
(三)Java工程化
GIT学习参考:https://gitscm.com/book/zh/v2(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgitscm.com%2Fbook%2Fzh%2Fv2)版本控制版本控制记录了一个或若干文件的历史变化,便于今后查阅,恢复。三类
3A网络 3A网络
2年前
Golang 常见设计模式之装饰模式
Golang常见设计模式之装饰模式想必只要是熟悉Python的同学对装饰模式一定不会陌生,这类Python从语法上原生支持的装饰器,大大提高了装饰模式在Python中的应用。尽管Go语言中装饰模式没有Python中应用的那么广泛,但是它也有其独到的地方。接下来就一起看下装饰模式在Go语言中的应用。简单装饰器我们通过一个简单的例子来
京东云开发者 京东云开发者
12个月前
chrome插件新版本(v3版本)中的热更新,即加载更新远程js的方法探索
1主流方案及尝试现在浏览器插件中,大多采用直接调用远程代码的方式进行热更新,由于安全策略逐步增强,越来越不支持热更新了;chrome在新v3版本插件中直接给禁止了;对于v2版本则即将废弃使用。或者要翻墙到google应用商店提交审核,才能热更新。2当前问题
程序员一鸣 程序员一鸣
3天前
鸿蒙开发:了解@Builder装饰器
@Builder装饰是鸿蒙UI开发中,非常重要的一个装饰器,在实际的开发中,合理且正确的使用,能够让我们的代码更加的简洁,有两点需要注意,一是,是用私有还是全局,取决于当前的组件的复用机制,如果多个页面都使用了,建议以全局为主;二是传参的动态更新,有更新就使用引用参数传递,没有更新按值传递即可。
程序员一鸣 程序员一鸣
3天前
鸿蒙开发:熟知@BuilderParam装饰器
所以在有@BuilderParam传递UI视图时,一定要注意this的指向问题,这也是为什么很多同学遇到在@Builder修饰的函数中为什么不刷新数据的问题,其原因就是this指向不对。