【HarmonyOS 5】鸿蒙的装饰器原理和自定义装饰器

简介: 一、鸿蒙中的装饰器是什么?在ArkTS中装饰器(Decorator)是一种特殊的声明,能够对类、方法、属性等进行标注和修改。


##鸿蒙开发能力 ##HarmonyOS SDK应用服务##鸿蒙金融类应用 (金融理财#

一、鸿蒙中的装饰器是什么?

在ArkTS中装饰器(Decorator)是一种特殊的声明,能够对类、方法、属性等进行标注和修改。

因为ArkTS 是TypeScript 扩展而来的编程语言,TypeScript 支持装饰器特性。它属于元编程的一种工具,可在不改变原有代码结构的基础上,为其添加额外的功能。比如在鸿蒙开发里,装饰器能够用来定义组件的属性、生命周期方法等。像@Component装饰器就用于把一个类标记成鸿蒙的组件类。

鸿蒙中的装饰器有状态装饰器V1和V2(@State、@Prop、@Link、@ObservedV2等等)。组件装饰器@Entry,@CustomDialog,@Component,@Builder等等。

详情参见官方文档:

状态管理(V1)
https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-state-management-v1

状态管理(V2)
https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-state-management-v2

二、装饰器的基本原理

ArkTS通过装饰器的方式,调用函数实现。在不侵入原有代码结构的基础上,进行扩展。

装饰器一般分为三种:类装饰器,方法装饰器,属性装饰器。

类装饰器在类之上声明,接收类的构造函数作为参数,可用于修改类的构造函数、添加类的属性和方法,或者对类进行一些元数据的标注:

function logDecorator(constructor: Function) {
  console.log(`Class ${constructor.name} is created.`);
}
@logDecorator
class MyComponent {
  constructor() {
    console.log('MyComponent instance is created.');
  }
}
const myComponent = new MyComponent();

方法装饰器应用于类的方法,在方法被定义时执行。它接收三个参数:目标对象、方法名和方法描述符。方法装饰器可以用于修改方法的行为,比如添加日志、进行权限验证、实现节流防抖等功能。在OpenHarmony开源系统中,对照系统相机源码,可看到以下自定义方法类如下所示。但是在HarmonyOS中,ArkTS对any强类型校验不通过。目前这种写法无法使用。

/*
 * Copyright (c) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { Log } from '../../utils/Log';
const TAG = '[Decorators]:'
export function debounce(timeout: number) {
  return function inner(target: any, propKey: string, descriptor: PropertyDescriptor) {
    let curFunc: number = 0;
    const original = descriptor.value;
    descriptor.value = function (...args: string[]) {
      Log.log(`${TAG} debounce invoke ${propKey} curFunc: ${curFunc}`);
      curFunc && clearTimeout(curFunc);
      curFunc = setTimeout(() => original.call(this, ...args), timeout);
    };
  };
}
export function throttle(waitTime: number) {
  return function inner(target: any, propKey: string, descriptor: PropertyDescriptor) {
    let lastTime: number = 0;
    const original = descriptor.value;
    descriptor.value = function (...args: string[]) {
      let curTime = Date.now();
      Log.log(`${TAG} throttle invoke ${propKey} timeInterval: ${curTime - lastTime}`);
      if (curTime - lastTime >= waitTime) {
        original.call(this, ...args);
        lastTime = curTime;
      }
    };
  };
}


属性装饰器应用于类的属性,在属性被定义时执行。它接收两个参数:目标对象和属性名。属性装饰器可以用于修改属性的访问器,比如添加属性验证逻辑、实现属性的缓存等。
但是在HarmonyOS中,ArkTS对any强类型校验不通过。目前这种写法无法使用。

function positiveNumber(target: any, propertyKey: string) {
  let value: number;
  const getter = function () {
    return value;
  };
  const setter = function (newValue: number) {
    if (newValue < 0) {
      throw new Error(`${propertyKey} must be a positive number.`);
    }
    value = newValue;
  };
  Object.defineProperty(target, propertyKey, {
    get: getter,
    set: setter,
    enumerable: true,
    configurable: true
  });
}
class MyModel {
  @positiveNumber
  age: number = 20;
}
const myModel = new MyModel();
myModel.age = -1; // 会抛出异常

三、在HarmonyOS中如何自定义装饰器

综上所述,在HarmonyOS中有特殊的ArkTS语法规则,any unknown这些不能使用。所以我们需要通过Object代替targe的any类型。

PropertyDescriptor中的value值也是any,直接按照ts的语法是没问题。但是在ArkTS中我们需要曲线实现目标,通过Function的形式获取value属性的值,再将target: Object, key: string, descriptor: PropertyDescriptor通过...args的形式赋值。

...args 是 JavaScript 和 TypeScript(ArkTS 基于 TypeScript)中的剩余参数(Rest Parameters)语法。

在HarmonyOS中定义类装饰器的方式如下所示,自定义属性装饰器同理:

// 自定义方法装饰器:记录方法调用信息
function methodLogger(target: Object, key: string, descriptor: PropertyDescriptor) {
  const originalMethod: Function = descriptor.value;
  descriptor.value = (...args: Object[]) => {
    // 获取被装饰方法的名称、入参、返回值
    console.log(`Calling ${target.constructor.name} method ${key} with argument: ${args}`)
    const result: Object = originalMethod(...args)
    console.log(`Method ${key} returned: ${result}`)
    return result
  }
  return descriptor;
}
@Entry
@Component
struct DecoratorDemoComponent {
  @State message: string = 'Hello HarmonyOS';
  // 使用自定义装饰器
  @methodLogger
  private processData(input: string): string {
    console.log('正在处理数据...');
    return `处理后的数据:${input.toUpperCase()}`;
  }
  // 组件显示时触发方法调用
  aboutToAppear() {
    const processedResult: string = this.processData('decorator demo');
    console.log('最终结果:', processedResult);
    this.message = processedResult;
  }
  build() {
    Column({ space: 30 }) {
      Text(this.message)
        .fontSize(24)
        .margin(10);
      Button('点击触发方法')
        .onClick(() => this.processData('button click'))
        .margin(10);
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center);
  }
}
目录
相关文章
|
4月前
|
容器
鸿蒙仓颉开发语言实战教程:自定义tabbar
本文介绍了在仓颉语言中实现自定义 tabbar 的完整过程。由于仓颉的 tabbar 组件限制较大,仅支持图片和文字,无法满足复杂样式需求,因此需手动构建带图标与文字的 tabbar 元素,并通过状态判断实现选中效果及点击切换功能。结合 Tabs 容器完成页面切换,适用于 HarmonyOS 商城应用开发。
|
4月前
|
开发者
鸿蒙仓颉开发语言实战教程:自定义组件
本文介绍了如何在仓颉开发语言中创建自定义组件,以封装和管理项目中的 tabbar 组件为例。通过创建独立的组件文件 yltabbar.cj,并使用 @Component 和 @Link 等修饰符实现组件化开发与参数传递,提升代码复用性和项目可维护性。适合希望深入掌握仓颉语言组件开发的 HarmonyOS 开发者学习参考。 #HarmonyOS #仓颉 #购物
|
20天前
|
JavaScript
鸿蒙应用开发从入门到实战(八):ArkTS自定义组件语法
ArkUI除系统预置的组件外,还支持自定义组件。使用自定义组件,可使代码的结构更加清晰,并且能提高代码的复用性。
|
4月前
|
开发者
Uniapp开发鸿蒙应用教程之自定义导航栏
本文介绍了在Uniapp跨平台开发鸿蒙应用时,如何实现自定义导航栏。通过修改pages.json文件可调整默认导航栏样式,但若需添加组件(如搜索框、按钮等),则需手动创建自定义导航栏组件,以适配鸿蒙系统。文中给出了详细的配置代码与实现步骤,帮助开发者灵活控制界面布局。#鸿蒙 #Uniapp #跨平台开发
|
6月前
|
人工智能 物联网 Android开发
【04】优雅草星云物联网AI智控系统从0开发鸿蒙端适配-deveco studio-自定义一个设置输入小部件组件-完成所有设置setting相关的页面-优雅草卓伊凡
【04】优雅草星云物联网AI智控系统从0开发鸿蒙端适配-deveco studio-自定义一个设置输入小部件组件-完成所有设置setting相关的页面-优雅草卓伊凡
317 92
|
4月前
|
索引
鸿蒙开发:自定义切换动画实现Swiper层叠滑动效果
customContentTransition不仅仅可以实现平移上的改变,很多的效果,我们都可以实现,比如放大缩小,旋转等等。
198 2
鸿蒙开发:自定义切换动画实现Swiper层叠滑动效果
|
5月前
|
存储 前端开发 Android开发
鸿蒙开发:自定义一个联系人模版
实现的方式并不是一成不变,你也可以通过Canvas自定义绘制来实现,基本上大同小异,都是必须要确认当前触摸字母的位置,然后进行样式的更改,左右列表的联动操作。
鸿蒙开发:自定义一个联系人模版
|
5月前
|
定位技术 开发者
HarmonyOS实战:高德地图自定义定位图标展示
本文详细介绍了在鸿蒙系统中实现地图定位功能的开发流程与注意事项。首先,开发者需要申请两个必要的定位权限,并确保用户手动开启系统设置中的位置权限。接着,通过高德定位获取用户位置信息,并使用自定义图标替代默认的定位箭头。文章特别强调了经纬度数据必须为float类型,否则可能导致定位不准确。此外,还需检查系统的GPS定位按钮是否开启,以确保定位功能正常使用。通过本文的指导,开发者可以避免常见的定位问题,顺利完成地图定位功能的开发。
211 2
HarmonyOS实战:高德地图自定义定位图标展示
|
4月前
|
移动开发 开发工具 容器
【HarmonyOS NEXT】鸿蒙使用ScanKit实现自定义扫码 (一)之业务流程和扫码
1)鸿蒙提供的ScanKit具备以下五种能力: 1. 扫码直达 2. 自定义扫码,图像识码 (自定义扫码需要这两种能力组合在一起,所以我分类在一起) 3. 码图生成 4. 系统提供的默认界面扫码
117 0
|
开发者
鸿蒙仓颉语言开发教程:自定义弹窗
假期第一天,祝大家端午节快乐!继续分享仓颉语言开发教程,介绍如何通过CustomDialogController实现自定义弹窗,包括参数设置与代码示例,帮助开发者掌握弹窗的使用技巧。