当前位置: 首页 > news >正文

HarmonyOS之深入了解装饰器

一、TypeScript 装饰器(Decorator)

1. 概述

装饰器是 ES 装饰器提案(Decorator Proposal) 的实现,它是 TypeScript 中的实验性特性(需启用),主要用于 对类、方法、属性等添加元编程能力

// tsconfig.json
{"experimentalDecorators": true,"emitDecoratorMetadata": true
}

2. 装饰器的类型

装饰器类型目标执行时机可访问信息
类装饰器类定义时构造函数
属性装饰器类的属性类定义时类原型、属性名
方法装饰器类的方法类定义时原型、方法名、属性描述符
访问器装饰器getter/setter类定义时原型、访问器名、属性描述符
参数装饰器方法参数类定义时原型、方法名、参数位置

3. 执行顺序(重要)

多个装饰器的执行顺序:

@ClassDeco
class MyClass {@PropertyDecomyProperty: string;@MethodDecomyMethod(@ParamDeco param: string) { }
}

执行顺序为:

  1. 参数装饰器
  2. 方法装饰器
  3. 属性装饰器
  4. 类装饰器

且从下往上执行多个装饰器(即先内后外)。


4. 每种装饰器的实现机制

a. 类装饰器

function Logger(constructor: Function) {console.log("类装饰器触发", constructor.name);
}
  • 装饰器函数接收构造函数作为参数;
  • 可以返回一个新的构造函数(替换类);
  • 适合用于依赖注入、静态注册、元信息记录等场景。

b. 方法装饰器

function Log(target: any, methodName: string, descriptor: PropertyDescriptor) {const original = descriptor.value;descriptor.value = function (...args: any[]) {console.log(`调用 ${methodName} 参数:`, args);return original.apply(this, args);};
}
  • 装饰的是方法;
  • 可用来包装逻辑,如日志记录、性能监控、错误捕获。

c. 属性装饰器

function Watch(target: any, propertyKey: string) {console.log(`属性 ${propertyKey} 被定义在:`, target);
}
  • 不能获取属性值;
  • 主要用于元信息注册(如序列化字段、ORM 映射)。

d. 参数装饰器

function Inject(target: any, method: string, index: number) {console.log(`${index}个参数被装饰`);
}
  • 可用于依赖注入容器中识别参数依赖。

5. 元数据机制(emitDecoratorMetadata + reflect-metadata)

配合 emitDecoratorMetadata: true,TS 会自动注入类型信息元数据,供运行时使用:

function LogType(target: any, key: string) {const type = Reflect.getMetadata("design:type", target, key);console.log(`${key} 类型是:`, type.name);
}

依赖 reflect-metadata 库,该库实现了 Reflect API 和元信息存储机制。


6. 本质实现流程(底层)

TypeScript 编译器做了什么?

  • 识别装饰器语法;
  • 按照目标(类、方法等)插入调用;
  • 若开启 emitDecoratorMetadata,会插入 Reflect.metadata(...) 调用;
  • 所有装饰器代码 保留在运行时代码中执行

关键特性:

  • 完全运行时执行;
  • 动态性强,但性能开销也存在;
  • 可构建框架功能(如 NestJS、TypeORM 全靠它)。

二、ArkTS 装饰器

1. 背景与定位

ArkTS 是 OpenHarmony(鸿蒙系统)为其 UI 框架定制的 TypeScript 方言,装饰器不是用于“扩展元编程能力”,而是 用于状态声明、组件通信、生命周期管理的 DSL 语义标记

装饰器是告诉编译器:这里是状态,这里是绑定,这里是通信入口。


2. 装饰器类型及其语义行为(编译期语义,重点)

装饰器作用范围编译器行为描述
@State组件内部变量生成 getter/setter,自动追踪值变化,触发 UI 更新
@Prop父传子属性父值传入子组件,只读,单向同步
@Link父子共享属性双向同步,子组件改值会同步给父组件
@Provide提供跨层级状态生成上下文注入代码
@Consume使用跨层级状态注入 Provide 提供的值
@ObservedV2响应式类标记深度监听类中属性
@Trace标记需监听的字段用于 @ObservedV2 类内部
@LocalStorage本地状态持久化编译生成持久化代码(通过本地 KV 存储)
@AppStorage全局状态存储跨组件共享状态,支持持久化
@Builder构建函数注册编译生成可复用的 UI 构造器
@Styles, @Extend样式注入编译为内部样式调用

3. 编译机制详解(核心)

与 TypeScript 装饰器不同,ArkTS 装饰器是编译器指令,不是运行时代码。

ArkCompiler 处理流程:

  1. 解析装饰器:构建 AST(抽象语法树)后,发现 @State@Link 等装饰器;

  2. 绑定语义处理器:每种装饰器都关联特定代码生成逻辑;

  3. 生成代码逻辑

    • @State:创建 getter/setter,并绑定变化监听逻辑;
    • @Link:生成数据绑定结构,修改值时同时刷新父子组件;
    • @Prop:设置为只读,插入一段更新依赖结构;
    • @ObservedV2:修改类的原型链,让其属性变化也会触发通知;
  4. 插入 UI 更新逻辑:绑定变量变化 → 触发 UI 重新渲染 → 更新虚拟 DOM → 重新绘制。


4. 例子:编译器到底“变出了什么”

@Component
struct Counter {@State count: number = 0;build() {Text(this.count.toString());Button("Add", () => this.count++);}
}

编译器背后大致生成:

let _count = 0;
function get_count() { return _count; }
function set_count(v) {_count = v;__triggerUIUpdate();  // 插入 UI 更新代码
}

Text() 内绑定的是 getter,this.count++ 实际调用的是 setter,UI 更新由编译器处理。


5. 装饰器之间的交互机制

例如:

@ObservedV2 class Address { @Trace street = ''; }
@Component struct A {@State addr: Address = new Address();
}
@Component struct B {@ObjectLink addr: Address;
}
  • 编译器发现 @ObservedV2 → 为 Address 生成监听结构;
  • @ObjectLink → 绑定状态引用;
  • 修改 addr.street → 自动触发 A/B 组件 UI 更新;
  • 若整体替换 addr → 同步被断开(需深拷贝保护)。

6. 核心特点(和 TS 装

饰器对比)

特性TypeScript 装饰器ArkTS 装饰器
执行时机运行时执行编译期处理
类型扩展任意目标仅限组件相关语义
灵活度极高受限于框架设计
运行开销有运行期反射负担无,提前生成逻辑
主要目的元编程、通用逻辑状态管理、UI更新
扩展方式写逻辑函数不能自定义,只能用框架内置装饰器

总结:TypeScript vs ArkTS 装饰器(深入+对比)

方面TypeScript 装饰器ArkTS 装饰器
本质运行时代码增强函数编译期语义标记(类似 DSL)
目标范围类、属性、方法、参数等组件状态、通信、样式、响应式对象等
编译器行为保留运行时代码,靠 Reflect 机制删除装饰器,生成 UI 状态同步逻辑
用户扩展性可自由实现装饰器逻辑受限,只能使用系统定义的装饰器
开发定位通用语言特性UI DSL 编译指令
使用效果功能灵活,逻辑自由开发效率高,结构清晰,性能高,但灵活性低
http://www.dtcms.com/a/351149.html

相关文章:

  • 服务器初始化流程***
  • Rust 符号体系全解析:分类、应用与设计意图
  • CentOS 7 升级 OpenSSL 3.5.1 的详细教程
  • 【Linux】Socket编程——TCP版
  • 【Python】shutil.make_archive() 方法详解
  • 支持向量机(SVM)核心原理与应用解析
  • SOME/IP-SD规范中,对 服务(Service) 和 实例(Instance)的理解
  • 多模态RAG架构:下一代跨模态智能检索系统的设计与实践
  • 机器视觉学习-day03-灰度化实验-二值化和自适应二值化
  • 使用C++与Qt6,在windows上打造MacOS风格桌面应用窗口
  • PDF文件中的相邻页面合并成一页,例如将第1页和第2页合并,第3页和第4页合并
  • Mac测试端口连接的几种方式
  • 如何将视频从安卓设备传输到Mac?
  • Mac安装mitmproxy及操作对监控的请求
  • 少儿舞蹈小程序详细设计文档
  • Mac中修改Word的Normal.dotm文件
  • 使用Uniapp开发小程序,如何引入插件组件!
  • 三电平buckboost电路出现上下母线不平衡是什么原因
  • Linux驱动开发笔记(八)——按键输入实验
  • 滚珠导轨如何定义半导体制造精度?
  • 【LeetCode 热题 100】75. 颜色分类——双指针
  • 算法题打卡力扣第209题:长度最小的子数组(mid)
  • 计算神经科学数学建模编程深度前沿方向研究(中)
  • AbMole小课堂丨Lenvatinib(E7080):如何通过靶向多靶点抑制VEGFR/FGFR/PDGFRα抑制肿瘤?
  • 【vue eslint】报错:Component name “xxxx“ should always be multi-word
  • LeetCode 100 -- Day6
  • 论文阅读:CIKM 2024 Empowering Private Tutoring by Chaining Large Language Models
  • 低空经济产业白皮书:音视频链路在智能飞行体系中的核心地位
  • 验证码请求与缓存问题解决方案
  • 用无标签语音自我提升音频大模型:SI-SDA 方法详解