Nest —— Providers

2,106 阅读7分钟

前言

       之前匆匆写了一篇关于Nest官文的翻译,然而写的时候比较着急,所以很多地方的翻译比较马虎,甚至直接丢进翻译器在丢出来....(有的时候我会被丢进翻译器之后被丢出来的译文吓到,原因是...译文比自己翻译的好/(ㄒoㄒ)/~~)但还是有很多部分是翻译器解决不了的,语句通顺固然优雅锦上添花,但一点小的错误却是致命的。
       现在手头比较悠闲,也打算重新修改一份比较优雅的中文文档。有人会问花这么多时间写这个东西,是不是真的有用。百度上面也有一些关于Nest的文档,完全也不会有人来看你写的翻译。我的感受是,可能这就是我的学习方式吧。其实平时阅读文档,大多数情况下都是脑子说会了,手说不会。一边翻译英文文档,一遍理解框架的含义,还有助于提高阅读英文文档的能力。手敲过一遍和眼睛看过一遍真的不太一样,而且这种方式会增加使用时的自信。之后打算将每一章节分开书写,最后再通过链接汇总到一篇Nest妲己大记中去。就算没有人看,自己想要翻阅文档的时候,也可以拿出来看看,还能改改。这是一种乐趣,就好像养成游戏一样。



正文 Providers

Providers(提供者)(我始终觉得提供者这个中文没有办法很好地表达providers的含义,同时也找不到更加满意的词来表达它的含义,所以我干脆比较喜欢就叫他Providers,后文不在提及提供者,而是用Providers代替)在Nest中是一个基础的概念。许多Nest基础的类都可以看作一个Providers—— services、repositories、factories、helpers等。 Providers主要指的是可以注入的依赖。 这意味着对象之间可以创建许多不同的依赖关系,而连线对象实例的功能可以主要委托给Nest的运行系统来处理。providers就是一个简单的使用了装饰器 @Injectable 的类。

前面的章节我们建立了一个简单的控制器 CatsController 。控制器只应处理HTTP请求,而把更多复杂的任务交给ProvidersProviders是简单地在类声明之前添加了装饰器 @Injectable 的类。

Hints
由于Nest可以通过一种更加面向对象的方式来设计和组织依赖,所以我们极力推荐你遵循设计模式中的SOLID原则



Services 服务

服务将负责数据的存储和检索,它是被设计用来被控制器 CatsController 使用的,所以它是一个被定义为Provider的最佳候选。

import { Injectable } from '@nestjs/common'
import { Cat } from './interfaces/cat.interface'

@Injectable()
export class CatsService {
  private readonly cats: Cat[] = []
  
  create(cat: Cat) {
    this.cats.push(cat);
  }
  
  findAll(): Cats[] {
    return this.cats;
  }
}

Hints
使用CLI创建service:$ nest g service cats

现在我们的 CatsService是拥有一个属性和两个方法的基础的类,唯一的新的特点是使用了装饰器 @Injectable ,这个装饰器 @Injectable 附加了metadata元数据,它告诉了Nest这个类是一个Nest的Provider。然后这里使用了 Cat 接口,它应该看起来是这样的

export interface Cat {
  name: string;
  age: number;
  breed: string;
}

现在拥有了一个服务类来检索cats,那么在 CatsController 中使用:

import { Controller, Get, Post, Body } from '@nestjs/common'
import { CreateCatDto } from './dto/create-cat.dto'
import { CatsService } from './cats.service'
import { Cat } from './interfaces/cat.interface'

@Controller('cats')
export class CatsController {
  constructor(
    private readonly catsService: CatsService,
  )
  
  @Post()
  async create(@Body() createCatDto: CreateCatDto) {
    this.catsService.create(createCatDto)
  }
  
  @Get()
  async findAll(): Promise<Cat[]> {
    return this.catsService.findAll()
  }
}

CatService 通过类的constructor注入到控制器中。注意这个语法: private readonly 它使得 catsService 这个类的成员在声明的同时初始化。(ts提供的语法糖)



DI —— Dependency injection 依赖注入

Nest是围绕着强大的设计模式,众所周知的DI(依赖注入)而构建的。关于他的概念我们建议阅读Augular的官方文档。
在Nest中,多亏了ts,管理项目中的依赖会非常简单,因为他们仅仅通过类型来被解析。在下面的例子中,Nest将通过创建并返回一个 CatsService 类的实例来解析 catsService (或者,在正常情况下是一个单例对象,返回一个如果在别处已经使用过的已经存在的实例)。这个依赖会被解析并传入你的控制器的构造器(constructor)中(或者被分配给声明的属性)。

constructor(private readonly catsService: CatsService) {}



Scopes 作用域

Providers一般都有一个和应用的生命周期同步的生命周期("scope")。当应用启动时,所有依赖都会被解析,然后每一个Provider会进行初始化。类似的当应用关闭,每个Provider将被销毁。但是也有一些方法可以使你的Provider的生命周期由你所需。这里有相关的技术支持(ing)



自定义Providers

Nest有一个内置的反向控制容器来解析Providers之间的关系。这个特征是上述依赖注入特性的基础,但实际上要比之前所描述的特性要强大的多。装饰器 @Injectable() 仅仅只是冰山一角,也不是定义Providers的唯一方式。实际上你可以用单纯的值,类,甚至是同步或异步的工厂。这里有更多的案例(ing)



可选的Providers

你可能偶尔有一些不需要解析的依赖。比如,你的类依赖于一个可配置的对象,但如果没有传入相应的配置,就应当使用默认的值。如此,依赖变成了可选项,而因为缺少了配置信息的provider不应该报错。
在构造器的签名中使用装饰器 @Optional 来表明provider是可选的。

import { Injectable, Optional, Inject } from '@nestjs/common'

@Injectable
export class HttpService<T> {
  constructor(
    @Optional() @Inject('HTTP_OPTIONS') private readonly: httpClient: T
  ) {}
}

注意上例我们使用了一个自定义Provider,这就是我们包含了HTTP_OPTIONS自定义token的原因。前例展示了基于构造器的依赖注入是通过在类的构造器中来指示依赖的。更多关于自定义Providers和他们相关的token(ing)



Property-based injection 基于属性的注入

在一些特殊的场合,基于属性的注入可能更加好用。比如,如果顶层类依赖于一个或多个其他的Providers,在子类的构造器中使用super()传入所有的依赖的操作会非常冗余。为了避免这种情况,可以在属性层使用装饰器 @Inject()

import { Injectable, Inject } from '@nestjs/common'

@Injectable()
export class HttpService<T> {
  @Inject('HTTP_OPTIONS')
  private readonly httpClient: T
}

Warning
如果你的类没有扩展其他的Provider,你应始终优先使用基于构造器的注入。



注册Provider

我们需要先注册定义好的服务,Nest才能注入他们。在模块文件中添加服务到providers数组中即可。

// app.module.ts
import { Module } from '@nestjs/common'
import { CatsController } from './cats/cats.controller'
import { CatsService } from './cats/cats.service'

@Module({
  controllers: [CatsController],
  providers: [CatsService],
})

export class AppModule {}

现在Nest将会解析 CatsController 中的依赖。
目录结构看起来像这样:





后记

原文地址: docs.nestjs.com/providers


关于本文

  • 文章非复制黏贴,经浏览文档,以自己的理解进行,代码测试,手打书写。本篇为翻译+意译。
  • 用作记录自己曾经学习、思考过的问题的一种笔记。
  • 用作前端技术交流分享。
  • 阅读本文时欢迎随时质疑本文的准确性,将错误的地方告诉我。本人会积极修改,避免文章对读者的误导。

关于我

  • 是一只有梦想的肥柴。
  • 觉得算法、数据结构、函数式编程、js底层原理等十分有趣的小前端。
  • 志同道合的朋友请关注我,一起交流技术,在前端之路上共同成长。
  • 如对本人有任何意见建议尽管告诉我哦~ 初为肥柴,请多多关照~
  • 前端路漫漫,技术学不完。今天也是美(diao)好(fa)的一天( 跪了...orz