装饰器-Vue.js官方基于它创建了一个库

131 阅读4分钟

vue-class-component 是一个用于在 Vue.js 中使用类风格组件的库。它允许你使用 TypeScript 装饰器来定义 Vue 组件,使代码更加简洁和可维护。结合vue-property-decorator 社区库使用常用的装饰器,例如 @Prop@Watch等。

定义

装饰器(Decorator)是 TypeScript 和 JavaScript 中的一种特殊语法,用于修改类、方法、属性或参数的行为。装饰器可以看作是一个函数,它可以在不修改原始代码的情况下,动态地为代码添加功能。

装饰器的类型

  1. 类装饰器:用于类声明之前,应用于类构造函数,可以用来监视、修改或替换类定义。
  2. 方法装饰器:用于方法声明之前,应用于方法的属性描述符,可以用来监视、修改或替换方法定义。
  3. 访问器装饰器:用于访问器(getter/setter)声明之前,应用于访问器的属性描述符。
  4. 属性装饰器:用于属性声明之前,应用于类的属性。
  5. 参数装饰器:用于参数声明之前,应用于方法的参数。

应用

tsconfig.json中需要开启这两项

 "experimentalDecorators": true,
 "emitDecoratorMetadata": true

介绍五种装饰器

1. 类装饰器

类装饰器用于类声明之前,应用于类构造函数,可以用来监视、修改或替换类定义。

入参

类装饰器只有一个入参,即被装饰的类的构造函数。

应用场景

添加或修改类的属性和方法: - 可以在类的原型上添加新的属性或方法,或者修改现有的属性和方法。

依赖注入: - 可以在类实例化时注入依赖,常用于依赖注入框架。

元数据添加: - 可以为类添加元数据,用于描述类的行为或属性。

类的替换: - 可以返回一个新的构造函数,从而替换原有的类。

下面示例使用的场景是添加或修改类的属性和方法

interface Person {
  school: string;
  identity: string;
}
@HighSchool()
class Person {}
type optionsType = {
  identity?: string;
  school?: string;
};
function HighSchool(options: optionsType = {}) {
  return function (target: any) {
    target.prototype.identity =
      options.identity ?? "senior high school student";
    target.prototype.school = options.school ?? "北京市第三中学";
  };
}
const p = new Person();
console.log(p.identity); // 输出 senior high school student
console.log(p.school);  // 输出北京市第三中学

2.方法装饰器

入参

方法装饰器有三个入参,分别是:

  1. target:被装饰方法所属的类的原型对象(对于实例方法)或类的构造函数(对于静态方法)。
  2. propertyKey:被装饰方法的名称。
  3. descriptor:被装饰方法的属性描述符。
应用场景

日志记录:在方法调用前后记录日志,方便调试和监控。

性能监控:记录方法的执行时间,帮助优化性能。

权限控制:在方法执行前检查用户权限,确保只有授权用户才能执行某些操作。

输入验证:在方法执行前验证输入参数的合法性,防止错误输入。

节流和防抖:控制方法的调用频率,防止频繁调用导致性能问题。

缓存:缓存方法的返回值,避免重复计算,提高性能。

事务管理:在方法执行前后管理事务,确保数据的一致性和完整性。

下面示例实现了装饰器节流

function Throttle(wait: number, options: any = {}) {
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ): void {
    const originalMethod = descriptor.value;
    let timer: any = null;

    descriptor.value = function (...args: any[]) {
      if (timer) {
        options.waitFn && options.waitFn();
        return;
      }
      timer = setTimeout(() => {
        originalMethod.apply(this, args);
        timer = null;
      }, wait);
    };
  };
}

class Person {
  @Throttle(3000, {
    waitFn: () => {
      console.log("还不饿");
    },
  })
  eat(food: string = "apple") {
    console.log("eat", food);
  }
}
const highSchool = new Person();
setInterval(() => {
  highSchool.eat(); 
}, 1000);

实现了节流,输出如下:

image.png

3.访问器修饰符

入参

target

 被装饰访问器所属的类的原型对象(对于实例访问器)。
 对于静态访问器,则是类的构造函数。
 在示例中,[`target`](<> "Go to definition") 是 [`Person.prototype`](<> "Go to definition")。

propertyKey

    被装饰访问器的名称。在示例中,[`propertyKey`](<> "Go to definition") 是 `"money"`

descriptor

被装饰访问器的属性描述符。属性描述符是一个对象,包含以下属性:

    [`get`](<> "Go to definition"):访问器的 getter 方法。
    [`set`](<> "Go to definition"):访问器的 setter 方法。
    `enumerable`:是否可以枚举属性。
    `configurable`:是否可以删除或修改属性的属性描述符。

在示例中,descriptor 包含 get 和 set 方法。

class Person {
  _money: number = 100;
  @logAccess()
  get money() {
    return this._money;
  }
  set money(value: number) {
    this._money = value;
  }
}
function logAccess() {
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    const originalGet = descriptor.get;
    const originalSet = descriptor.set;

    if (originalGet) {
      descriptor.get = function () {
        console.log(`Getting value of ${propertyKey}`);
        return originalGet.apply(this);
      };
    }

    if (originalSet) {
      descriptor.set = function (value: any) {
        console.log(`Setting value of ${propertyKey} to ${value}`);
        originalSet.apply(this, [value]);
      };
    }
  };
}
const highSchool = new Person(); 
const getMoney = highSchool.money; // get Getting value of money
highSchool.money = 50; // Setting value of money to 50

4.属性修饰符

入参
  1. target:被装饰属性所属的类的原型对象(对于实例属性)或类的构造函数(对于静态属性)。
  2. propertyKey:被装饰属性的名称。

5.参数修饰符

入参

target:被装饰参数所属的方法的类的原型对象(对于实例方法)或类的构造函数(对于静态方法)。

propertyKey:被装饰参数所属的方法的名称。

parameterIndex:被装饰参数在方法参数列表中的索引。