写点什么

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

作者:GeorgeGcs
  • 2025-04-10
    上海
  • 本文字数:2937 字

    阅读完需:约 10 分钟

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

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

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


在 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.`);}
@logDecoratorclass 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@Componentstruct 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); }}
复制代码


发布于: 刚刚阅读数: 3
用户头像

GeorgeGcs

关注

路漫漫其修远兮,吾将上下而求索。 2024-12-24 加入

历经腾讯,宝马,研究所,金融。 待过私企,外企,央企。 深耕大应用开发领域十年。 OpenHarmony,HarmonyOS,Flutter,H5,Android,IOS。 目前任职鸿蒙应用架构师。 HarmonyOS官方认证创作先锋

评论

发布
暂无评论
【HarmonyOS 5】鸿蒙的装饰器原理和自定义装饰器_华为_GeorgeGcs_InfoQ写作社区