JS 装饰器概述
JavaScript 装饰器是一种以 @ 符号开头的特殊语法,放在目标代码的前面用于包装或扩展代码功能。
JavaScript 的装饰器语法目前仍处于提案阶段,现阶段使用的话需要通过 babel 等方式进行编译之后,才能在浏览器正常运行。
装饰器分为两种:类装饰器,类成员装饰器,分别用于装饰 类以及类的成员。
如何在vite项目中使用decorator
- 安装两个包
p i @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties
- vite配置
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [
react({
babel: {
plugins: [
["@babel/plugin-proposal-decorators", { version: "legacy" }],//利用stage1阶段语法
["@babel/plugin-proposal-class-properties", { loose: true }], // 视情况非必需, 该地方的vite配置中就不需
],
},
}),
],
});
-
使用不同的阶段,decorator语法不同
-
legacy为true就是stage 1阶段的提案,
-
在2018.9进入stage2阶段后的提案,用法已经完全和stage1不一样
-
最近正式进入stage3的提案
最新的@babel/plugin-proposal-decorators已经将该配置迁移到version,原来的legacy:true即version: 'legacy'
-
如果使用了
@babel/preset-envand Stage 3 decorators, 可不用plugin-proposal-class-properties,因为babel在presets之前,就对decorator自动进行了transform -
legacy英文是 形容词‘老式的’, 表示阶段是古老的; 名词‘遗产,遗存‘ { "compileOptions": { "experimentalDecorators": true } }
stage1 阶段语法:
type decorator = (
target: Target | Target.prototype,
propertyKey: string,
descriptor: PropertyDescriptor
) => Function | void;
stage3 阶段语法:
type Decorator = (value: Input, context: {
kind: string;
name: string | symbol;
access: {
get?(): unknown;
set?(value: unknown): void;
};
isPrivate?: boolean;
isStatic?: boolean;
addInitializer?(initializer: () => void): void;
}) => Output | void;
装饰器函数包含两个入参参数
1、被装饰的值本身
2、被装饰值的上下文信息
- kind :
"class"|"method"|"getter"|"setter"|"field"|"accessor"。表示装饰器的类型 - name 装饰值的名称
- access 同个该属性读写值
- isStatic 是否静态属性
- isPrivate 是否私有属性
- addInitializer 用于执行一些初始化逻辑
下面的介绍是基于stage1 的阶段的使用。目前是stage3阶段,使用方式不同
语法
装饰器语法分为两部分。
首先是装饰器的定义,装饰器定义是编写一个函数,函数会接受所装饰的内容作为参数。
类装饰器和类型成员装饰器只是在接收的参数上不同。
而装饰器的使用则是使用 @ 符号加上定义的装饰器名称(即装饰器的函数名)
// 定义一个类装饰器
const classDecorator = (target) => {
// do something
}
// 使用装饰器
@classDecorator
class Human {
}
类装饰器
类装饰器接收一个参数,即类的本身。
type decorator = (target: Function) => Function | void;
const classDecorator = (target) => {
console.log(`species is ${target.species}`); // species is human
}
// 使用装饰器
@classDecorator
class Human {
static species = 'human';
}
类成员装饰器
类成员可以是类的属性,方法,或者是 getter 或 setter 等;类成员装饰器函数接收三个参数,分别是类本身,成员名称,成员修饰符(就是表示属性的是否可枚举,可读,可配置等的对象)。
属性装饰器
type decorator = (
target: Target | Target.prototype,
propertyKey: string
) => void;
const speciesDecorator = (target, name, descriptor) => {
console.log({ target, name, descriptor });
}
class Human {
@speciesDecorator
static species = 'Human';
}
装饰器输出结果为:
比如我们可以利用 descriptor 这个参数来做限制属性为不可枚举
const speciesDecorator = (target, name, descriptor) => {
console.log({ target, name, descriptor });
descriptor.enumerable = false; // 将属性变为不可枚举
}
class _Human {
static species = 'Human';
}
console.log(Object.keys(_Human)); // ['species']
class Human {
@speciesDecorator
static species = 'Human';
}
console.log(Object.keys(Human)); // []
方法装饰器
type decorator = (
target: Target | Target.prototype,
propertyKey: string,
descriptor: PropertyDescriptor
) => Function | void;
方法装饰器的参数定义如下:
1、第一个参数。如果装饰的是静态方法,则是这个类Target本身;如果装饰的是原型方法,则是类的原型对象Target.prototype
2、第二个参数。这个方法的名称
3、第三个参数,这个方法的属性描述符,通过descriptor.value可以直接拿到这个方法
同时使用多个装饰器
装饰器是可以叠加使用的,使用多个装饰器只需要在代码前面添加多个装饰器的使用即可
const noEnumerable = (target, name, descriptor) => {
// 将属性改为不可枚举
descriptor.enumerable = false;
}
const readonly = (target, name, descriptor) => {
// 将属性改为只读
descriptor.writable = false;
}
class Human {
@noEnumerable
@readonly
static species = 'human';
}
console.log(Object.keys(Human)); // []
console.log(Human.species); // human
Human.species = 'new human';
console.log(Human.species); // human
-
装饰器的执行顺序
-
装饰器在
类定义时即执行 -
先执行类成员装饰器,类成员装饰器执行完后,再执行类装饰器。 -
类成员装饰器的先后执行取决于成员的定义顺序
-
const classDecorator = (target) => {
console.log('类装饰器执行')
}
const speciesDecorator_1 = (target, name, descriptor) => {
console.log('species_1 装饰器执行')
}
const speciesDecorator_2 = (target, name, descriptor) => {
console.log('species_2 装饰器执行')
}
// 使用装饰器
@classDecorator
class Human {
@speciesDecorator_1
static species_1 = 'human';
@speciesDecorator_2
static species_2 = 'human';
}
执行结果为:
species_1 装饰器执行
species_2 装饰器执行
类装饰器执行