likes
comments
collection
share

Nest 装饰器

作者站长头像
站长
· 阅读数 25
Nest 装饰器

在 NestJS 中,装饰器(decorator)无处不在,要想熟练的掌握 Nest 语法,掌握装饰器是必不可少的。

装饰器的基本原理

在 typescript 中, 装饰器是一种特殊类型的声明,它能够被附加到类声明方法属性以及参数上。

装饰器是一个表达式(express);该表达式被执行后,必须返回一个函数。它会在运行时被调用,被装饰的声明信息做为参数传入。

其写法:@express

牢记:装饰器执行后会返回一个函数

配置使用

装饰器是 ts 中的一个实验性特性,需要单独配置之后,才能才能使用。

第一种方式:命令行

# 命令运行配置
tsc --target ES5 --experimentalDecorators

第二种方式:配置 tsconfig.json

{
  "compilerOptions": {
    "target": "ES5",
    "experimentalDecorators": true // here
  }
}

类装饰器

用来修饰类。

类型申明文件

declare type ClassDecorator = <TFunction extends Function>(
  target: TFunction // target 就是构造函数
) => TFunction | void;

案例一:不传递参数

function Log(target: Function): void {
  target.prototype.log = function (): void {
    console.log("copyer");
  };
}

@Log // 使用语法糖装饰器,在运行是会注入
class Person {
  constructor() {}
}

const p = new Person();
p.log(); // 调用装饰器里面的函数

案例二:传递参数

function Log(info: string) {
  return function (target: Function): void {
    target.prototype.log = function () {
      console.log(info);
    };
  };
}

@Log("copyer") // 先执行 Log 函数,然后再返回一个函数,用于做装饰器表达式(闭包的应用场景)
class Person {
  constructor() {}
}

const p = new Person();
p.log();

属性装饰器

用于修饰类的属性。

可以针对属性获取或者设置时,进行一些操作(比如说:拦截)。

类型声明文件

declare type PropertyDecorator = (target: Object, key: string | symbol) => void;

案例一:拦截属性的修改

function Log(target: any, key: string) {
  delete target[key];
  const _key = "_" + key;
  Object.defineProperty(target, _key, {
    writable: true,
    enumerable: true,
    configurable: true,
  });

  const getter = function (this: any) {
    console.log("getter");
    return this[_key];
  };
  const setter = function (this: any, newValue: string) {
    console.log("setter");
    this[_key] = newValue;
  };
  // 重写 get 和 set 方法,进行拦截
  Object.defineProperty(target, key, {
    get: getter,
    set: setter,
    enumerable: true,
    configurable: true,
  });
}

class Person {
  @Log
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}

方法装饰器

用于修饰类的方法,进行操作。比如说,给该方法注入一些属性或者特性。

着重理解返回值 descriptor,属性描述符

类型申明文件

/**
 * target: 被装饰的类
 * key: 方法名
 * descriptor: 属性描述符
 */
declare type MethodDecorator = <T>(
  target: Object,
  key: string | symbol,
  descriptor: TypePropertyDescript<T>
) => void | TypePropertyDescript<T>;

案例一:获取旧的函数,赋值新的函数

function logName(target: Function, key: string, descriptor: any) {
  const originFn = descriptor.value; // 获取原始函数

  // 定义一个新的函数,进行赋值
  let newFn = function (...args: any[]) {
    return originFn.apply(this, args);
  };
  descriptor.value = newFn;
}

class Person {
  name: string;
  constructor(name: string) {
    this.name = name;
  }

  @logName
  getName() {
    return this.name;
  }
}

参数装饰器

用于针对方法中的属性,进行操作,装饰。

/**
 * target: 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
 * key:成员的名字
 * descriptor:参数在函数参数列表中的索引
 */
declare type ParamDecorator = <T>(
  target: Object,
  key: string | symbol,
  index: number
) => void;

案例一:简单打印

function addName(target: Object, key: string, index: number) {
  console.log(target); // {setName: Function } 类的原型对象
  console.log(key); // setName
  console.log(index); // 0
}

class Person {
  name: string;
  constructor(name: string) {
    this.name = name;
  }

  setName(@addName name: string) {
    this.name = name;
  }
}

学习完装饰器的基本语法之后,可以借助 babel 的转化,变成 es5,看看是怎么实现的。

内置装饰器

上面了解到装饰器的基本使用和原理。接下来就看看 nestjs 到底存在哪些装饰器。

这里只是先了解一下存在哪些装饰器,具体的用法不会过多分析,后续再后面学习过程中,逐渐了解。

  • @Module 申明模块
  • @Controller 申明控制器
  • @Injectable 申明提供者,中间件,守卫等等
  • @Inject 用于属性注入
  • @Optional 依赖可选
  • @Global 申明全局
  • @Catch 指定处理异常
  • @UseFilters 绑定过滤器
  • @UseGuards 绑定守卫
  • @UseInterceptors 绑定拦截器
  • @UsePipes 绑定管道
  • @Query 解析 query 参数
  • @Param 解析动态路由参数
  • @Post post 请求
  • @Get get 请求
  • @Put put 请求
  • @Delete delete 请求
  • @Body 解析 body 参数
  • @SetMetadata 指定元数据
  • @Headers 取出某个或全部请求头
  • @Header 设置请求头
  • @Ip 获取 IP
  • @Session 获取 session
  • @HostParam 获取 host 信息
  • @Request 或者 @Req request 对象
  • @Res 或者 @Response response 对象
  • @Next 中间件
  • @Redirect 重定向
  • @Render 指定渲染用的模版引擎
  • @HttpCode 修改状态码
  • @SetMetadata 设置元数据

自定义装饰器

当满足下面两种情况的一种时:

  • 当装饰器都不满足需求的时候,能不能自己开发呢?
  • 装饰器比较多的时候,能不能把多个装饰器合并成一个呢?

那么就可以采取自定义装饰器了。

合并装饰器

当遇到很多装饰器集中到一起时

Nest 装饰器

可以采取自定义装饰器,简化一下。

利用 applyDecorators 来合并装饰器。

import {
  applyDecorators,
  Get,
  Header,
  HttpCode,
  SetMetadata,
} from '@nestjs/common';

export const Aaa = (httpCode: number, metaValue: string) => {
  return applyDecorators(
    Get(),
    HttpCode(httpCode),
    SetMetadata('role', metaValue),
    Header('Cache-Control', 'none'),
  );
};

Nest 装饰器

自定义参数装饰器

Nest 还是提供了可以快速创建装饰器的函数createParamDecorator

export const Baa = createParamDecorator(
  /**
   * factory 函数
   * @param data 传递的参数
   * @param ctx 上下文
   */
  (data: string, ctx: ExecutionContext) => {
    return 'copyer';
  },
);

使用自定义装饰器

Nest 装饰器

也会发现:参数装饰器的返回值就是参数的值

了解了上述,那么针对一些参数装饰器(比如:@Header、@Query 等等)也可以自己简单实现。

模拟 @Headers 装饰器

// MockHeaders
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { Request } from 'express';

export const MockHeaders = createParamDecorator(
  (key: string, ctx: ExecutionContext) => {
    const request: Request = ctx.switchToHttp().getRequest();
    return key ? request.headers[key.toLowerCase()] : request.headers;
  },
);

模拟 @Query 装饰器

export const MockQuery = createParamDecorator(
  (key: string, ctx: ExecutionContext) => {
    const request: Request = ctx.switchToHttp().getRequest();
    return request.query[key];
  },
);

模拟 @Get() 方法装饰器

上面针对参数装饰器进行自定义,也可以简单了解一下方法装饰器的自定义(记住一点,执行装饰器表达式,返回一个函数)

const Get = (path: string) => {
  // 方法装饰器
  return (
    target: Object,
    key: string,
    descriptor: TypedPropertyDescriptor<any>
  ) => {
    // 保存函数 (getList)
    const saveFn = descriptor.value;
    // 发送请求,获取结果
    axios
      .get(path)
      .then((res) => {
        // 然后回调保存的函数
        saveFn({ data: res.data });
      })
      .catch((err) => {
        saveFn({ err });
      });
  };
};

class Controller {
  constructor() {}

  @Get("/cats")
  getList(res) {
    console.log("res======>", res);
  }
}

利用 axios 发送请求,然后获取数据,保存在属性描述符的函数中,在后续使用。

总结

  • Typescript 中的装饰器是一个实验室语法,需要自己配置 experimentalDecorators;其表达式为 @express,返回一个函数
  • 装饰器分为四种:类装饰器、方法装饰器、属性装饰器、参数装饰器。着重理解各个装饰器的参数及类型。
  • 总结了 nestjs 中的多个内置装饰器
  • 简单模拟实现了一下 @Get 方法装饰器实现。
  • 合并装饰器 applyDecorators
  • 自定义参数装饰器 createParamDecorator
转载自:https://juejin.cn/post/7364051847177289728
评论
请登录