赞
踩
我们介绍的也只是2014年通过的。如果想了解最新版的装饰器,请看 TypeScript 装饰器
装饰器简单代码示例
- @ClassDecorator() // 类装饰器
- class A {
-
- @PropertyDecorator() // 属性装饰器
- name: string;
-
- @MethodDecorator() // 方法装饰器
- fly(
- @ParameterDecorator() // 参数装饰器
- meters: number
- ) {
- // code
- }
-
- @AccessorDecorator() // 存取器装饰器
- get egg() {
- // code
- }
- set egg(e) {
- // code
- }
- }
- 注意,构造方法没有方法装饰器,只有参数装饰器。类装饰器其实就是在装饰构造方法。
- 装饰器只能用于类,要么应用于类的整体,要么应用于类的内部成员,不能用于独立的函数
TFunction
必须是函数,实际上就是构造方法。- type ClassDecorator = <TFunction extends Function>
- (target: TFunction) => TFunction | void;
- function Log(info:string) {
- console.log('received: ', info);
- return function (target:any) {
- console.log('apply decorator');
- return target;
- }
- }
-
- @Log('log something')
- class A {}
-
属性装饰器不需要返回值,如果有的话,也会被忽略
- type PropertyDecorator =
- (
- target: Object,
- propertyKey: string|symbol
- ) => void;
装饰器ValidRange
对属性year
设立了一个上下限检查器,只要该属性赋值时,超过了上下限,就会报错
- function ValidRange(min:number, max:number) {
- return (target:Object, key:string) => {
- Object.defineProperty(target, key, {
- set: function(v:number) {
- if (v < min || v > max) {
- throw new Error(`Not allowed value ${v}`);
- }
- }
- });
- }
- }
-
- // 输出 Installing ValidRange on year
- class Student {
- @ValidRange(1920, 2020)
- year!: number;
- }
-
- const stud = new Student();
-
- // 报错 Not allowed value 2022
- stud.year = 2022;
方法装饰器用来装饰类的方法。方法装饰器的返回值(如果有的话),就是修改后的该方法的描述对象,可以覆盖原始方法的描述对象
string|symbol
。- type MethodDecorator = <T>(
- target: Object,
- propertyKey: string|symbol,
- descriptor: TypedPropertyDescriptor<T>
- ) => TypedPropertyDescriptor<T> | void;
方法装饰器@logger
用来装饰add()
方法,它的作用是让该方法输出日志。每当add()
调用一次,控制台就会打印出参数和运行结果
- function logger(
- target: any,
- propertyKey: string,
- descriptor: PropertyDescriptor
- ) {
- const original = descriptor.value;
-
- descriptor.value = function (...args) {
- console.log('params: ', ...args);
- const result = original.call(this, ...args);
- console.log('result: ', result);
- return result;
- }
- }
-
- class C {
- @logger
- add(x: number, y:number ) {
- return x + y;
- }
- }
-
- (new C()).add(1, 2)
- // params: 1 2
- // result: 3
-
该装饰器不需要返回值,如果有的话会被忽略
string|symbol
。- type ParameterDecorator = (
- target: Object,
- propertyKey: string|symbol,
- parameterIndex: number
- ) => void;
- function log(
- target: Object,
- propertyKey: string|symbol,
- parameterIndex: number
- ) {
- console.log(`${String(propertyKey)} NO.${parameterIndex} Parameter`);
- }
-
- class C {
- member(
- @log x:number,
- @log y:number
- ) {
- console.log(`member Parameters: ${x} ${y}`);
- }
- }
-
- const c = new C();
- c.member(5, 5);
- // member NO.1 Parameter
- // member NO.0 Parameter
- // member Parameters: 5 5
-
存取器装饰器用来装饰类的存取器(accessor)。所谓“存取器”指的是某个属性的取值器(getter)和存值器(setter)
TypeScript 不允许对同一个属性的存取器(getter 和 setter)使用同一个装饰器,也就是说只能装饰两个存取器里面的一个,且必须是排在前面的那一个,否则报错
- type AccessorDecorator = <T>(
- target: Object,
- propertyKey: string|symbol,
- descriptor: TypedPropertyDescriptor<T>
- ) => TypedPropertyDescriptor<T> | void;
- function validator(
- target: Object,
- propertyKey: string,
- descriptor: PropertyDescriptor
- ){
- const originalGet = descriptor.get;
- const originalSet = descriptor.set;
-
- if (originalSet) {
- descriptor.set = function (val) {
- if (val > 100) {
- throw new Error(`Invalid value for ${propertyKey}`);
- }
- originalSet.call(this, val);
- };
- }
- }
-
- class C {
- #foo!: number;
-
- @validator
- set foo(v) {
- this.#foo = v;
- }
-
- get foo() {
- return this.#foo;
- }
- }
-
- const c = new C();
- c.foo = 150;
- // 报错
-
执行装饰器时,按照如下顺序执行。
@State装饰器只能应用于组件内部,并且它修饰的是属性。从这些特征我们就能知道它是一个属性装饰器
我们知道鸿蒙工程是一个多Module的工程,开发态和编译后的包对应视图如下
看一下我们目前的代码工程
进行代码编译成包
查看编译后的结构。可以看到我们编译成了一个.app和一个.hap
右键entry的hap,open in finder
复制一份.hap文件,并修改为.zip后缀,然后双击打开
ets文件夹里面的就是方舟字节码文件,使用010Editor进行解析(很抱歉,我没学会怎么用,借了一张大神的图).看到如下图所示
当我们用@Component修饰一个struct的时候,通过ArkCompiler编译后,其实会生成一个类,这个类继承于ViewPU
ViewPU定义在ArkTS framework arkui_ace_engine当中,是Openharmony中UI承载的关键类
看一下构造函数
- /**
- * Create a View
- *
- * 1. option: top level View, specify
- * - compilerAssignedUniqueChildId must specify
- * - parent=undefined
- * - localStorage must provide if @LocalSTorageLink/Prop variables are used
- * in this View or descendant Views.
- *
- * 2. option: not a top level View
- * - compilerAssignedUniqueChildId must specify
- * - parent must specify
- * - localStorage do not specify, will inherit from parent View.
- *
- */
- constructor(parent: ViewPU, localStorage: LocalStorage, elmtId : number = -1, extraInfo : ExtraInfo = undefined) {
- super();
- // if set use the elmtId also as the ViewPU object's subscribable id.
- // these matching is requiremrnt for updateChildViewById(elmtId) being able to
- // find the child ViewPU object by given elmtId
- this.id_= elmtId == -1 ? SubscriberManager.MakeId() : elmtId;
- this.providedVars_ = parent ? new Map(parent.providedVars_)
- : new Map<string, ObservedPropertyAbstractPU<any>>();
-
- this.localStoragebackStore_ = undefined;
- stateMgmtConsole.log(`ViewPU constructor: Creating @Component '${this.constructor.name}' from parent '${parent?.constructor.name}}'`);
- if (extraInfo) {
- this.extraInfo_ = extraInfo;
- }
- if (parent) {
- // this View is not a top-level View
- this.setCardId(parent.getCardId());
- // Call below will set this.parent_ to parent as well
- parent.addChild(this);
- } else if (localStorage) {
- this.localStorage_ = localStorage;
- stateMgmtConsole.debug(`${this.debugInfo()}: constructor: Using LocalStorage instance provided via @Entry.`);
- }
-
- SubscriberManager.Add(this);
- stateMgmtConsole.debug(`${this.debugInfo()}: constructor: done`);
- }
上面的注释写明了,@Component装饰的视图作为顶级视图和非顶级视图的情况
顶级视图选项:
compilerAssignedUniqueChildId
,用于唯一标识子视图。parent
设置为undefined
,表示这是顶级视图。@LocalStorageLink/Prop
变量,必须提供localStorage
。非顶级视图选项:
compilerAssignedUniqueChildId
,用于唯一标识子视图。parent
,指明父级视图。localStorage
,它将从父视图继承。看一下最后的代码 它调用SubscriberManager添加自身,后面state的回调会进行callback,同时它也有父子组件的概念,ViewPU有一个parent属性,代表当前的父组件,父子双方共用同一个localStorage,其实就是通过构造函数保证的。
我们看到属性改变的时候,调用了他所属view的viewPropertyHasChanged
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。