前言
之前匆匆写了一篇关于Nest官文的翻译,然而写的时候比较着急,所以很多地方的翻译比较马虎,甚至直接丢进翻译器在丢出来....(有的时候我会被丢进翻译器之后被丢出来的译文吓到,原因是...译文比自己翻译的好/(ㄒoㄒ)/~~)但还是有很多部分是翻译器解决不了的,语句通顺固然优雅锦上添花,但一点小的错误却是致命的。
现在手头比较悠闲,也打算重新修改一份比较优雅的中文文档。有人会问花这么多时间写这个东西,是不是真的有用。百度上面也有一些关于Nest的文档,完全也不会有人来看你写的翻译。我的感受是,可能这就是我的学习方式吧。其实平时阅读文档,大多数情况下都是脑子说会了,手说不会。一边翻译英文文档,一遍理解框架的含义,还有助于提高阅读英文文档的能力。手敲过一遍和眼睛看过一遍真的不太一样,而且这种方式会增加使用时的自信。之后打算将每一章节分开书写,最后再通过链接汇总到一篇Nest妲己大记中去。就算没有人看,自己想要翻阅文档的时候,也可以拿出来看看,还能改改。这是一种乐趣,就好像养成游戏一样。
正文 Modules
模块是使用装饰器
@Module()
的类。装饰器提供了元数据metadata
给Nest来组织整个应用的架构。
每个应用至少需要拥有一个根模块。根模块是Nest绘制整个应用图(内部的数据结构,Nest用于解析模块和providers之间的关系和依赖)的起始点。当然非常小的应用理论上来说确实可能只有一个根模块,但这并不是典型案例。我们想要强调的是, 模块是墙裂推荐来组织你的组件的一种十分高效的方式 。所以对大多数的应用来说,最终的结构会使用多个模块, 每一个模块都是对一系列紧密相关的功能的一个封装 。
装饰器 @Module() 接受一个用来描述模块的配置对象,包含以下属性:
providers | providers通过Nest的注入器实例化,然后至少在当前模块可以共享这些providers |
controllers | 定义在当前模块的一组需要被实例化的控制器 |
imports | 引入的模块的列表,由其他模块导出的providers并且在当前模块需要使用的模块 |
exports | providers的子集,由当前模块提供的provis,并且在其他引入当前模块的模块中应当能够使用 |
模块会默认将providers
封装进内部,这意味着你不能注入那些
既不是当前模块的部分,又不是从引入的模块中导出过的
providers
。所以你可能需要考虑从一个模块中导出一些providers
作为所有模块的公共接口或API
Feature modules 功能模块
CatsController
和
CatsService
属于相同的应用领域。既然他们紧密相关,将他们放进一个功能模块是具有意义的。功能模块只是简单地组织和特定功能相关的代码,保证了代码组织有序,边界清晰。这有助于我们处理复杂性,并且遵循SOLID
原则开发,尤其是当应用的大小随着团队人数而增长时。
// cats/cats.module.ts
import { Module } from '@nestjs/common'
import { CatsController } from './cats.controller'
import { CatsService } from './cats.service'
@Mdoule({
controllers: [CatsController],
providers: [CatsService],
})
export class CatsModules {}
Hints
使用CLI来创建模块:$ nest g module cats
上面我们在cats.module.ts
文件中定义了
CatsModule
模块,将所有和该模块相关的文件放入cats
目录下。最后要做的事情就是在跟模块中将这模块引入。
import { Module } from '@nestjs/common'
import { CatsModule } from './cats/cats.module'
@Module({
imports: [CatsModule],
})
export class AppModule {}
然后目录结构看起来是这样:
Shared modules 共享模块
在Nest中模块默认是一个单例对象,所以你可以在模块之间共享任何provider
的相同实例。
exports
数组中导出他
// cats.module.ts
import { Module } from '@nestjs/common'
import { CatsController } from './cats.controller'
import { CatsService } from './cats.service'
@Module({
controlers: [CatsController],
providers: [CatsService],
exports: [CatsService],
})
export class CatsModule {}
现在任何导入 CatsModule 的模块都可以去使用 CatsService ,并且也可以在其他模块之间共享同一个实例。
Module re-exporting 模块重导出
模块可以导出他们内部的providers
,模块也可以重导出他们引入的模块。如下例,
CommonModule
模块同时被
CoreModule
引入并导出,那么在其它引入
CoreModule
模块的地方也可以使用该模块。
@Module({
imports: [CommonModule],
exports: [CommonModule]
})
export class CoreModule {}
Dependency injection DI 依赖注入
模块的类也可以注入providers
import { Module } from '@nestjs/common'
import { CatsController } from './cats.controller'
import { CatsService } from './cats.service'
@Module({
controllers: [CatsController],
providers: [CatsServices],
})
export class CatsModule {
constructor(private readonly catsService: CatsService) {}
}
然而,模块的类本身不可以被当作providers
注入,会导致循环依赖。
Global modules 全局模块
到处引入相同的一组模块是冗余的,Agular
不像Nest,他的Providers
是注册在全局作用域中的。一旦定义任何地方都可以使用。但是Nest注册Providers
是封装进模块作用域的。如果没有引入对应的模块是无法使用这个模块下的Providers
的。
当你想要提供一组providers
任何地方都可以使用,为这个模块添加装饰器
@Global()
import { Module, Global } from '@nestjs/common'
import { CatsController } from './cats.controller'
import { CatsService } from './cats.service'
@Global()
@Module({
controllers: [CatsController],
providers: [CatsService],
exports: [CatsService],
})
export class CatsModule {}
@Global()
装饰器使这个模块成为全局作用域。
全局模块应该只注册一次
,通常是在根模块或者核心模块中。在上例,
CatsService
将无处不在,模块想要注入这个服务并不需要在
CatsModule
的imports
中引入。
Hints
全局模块是为了减少那些必须的引用,但不要把所有的东西都放入全局。使用imports
数组去引入模块的api始终是更推荐的方式。
Dynamic modules 动态模块
Nest模块系统包含了强大的特性叫做动态模块。这个特性可以使你简单地创建可动态注册配置地自定义模块。这里(ing)涵盖了大部分动态模块的内容。这个章节将简要概述,来完成模块的介绍。
下面是对
DatabaseModule
动态模块的定义的一个例子。
import { Module, DynamicModule } from '@nestjs/common'
import { createDatabaseProviders } from './database.providers'
import { Connection } from './connection.provider'
@Module({
providers: [Connection],
})
export class DatabaseModule {
static forRoot(entities = [], options?): DynamicModule {
const providers = createDatabaseProviders(options, entities)
return {
module: DatabaseModule,
providers: providers,
exports: providers,
}
}
}
Hints
forRoot()
方法可能同步或异步地返回一个动态模块。
这个模块默认定义了
Connection
provider,但是此外还会取决于传入
forRoot()
方法的entiteis
和options
对象,它暴露了一个providers
的集合,例如库。注意动态模块返回的这些属性,扩展了(甚至是重载)在装饰器
@Module()中定义的
基本的模块元数据。这就是如何同时从模块导出静态声明的Provider
Connection
和动态地生成数据库Provider
。
一旦定义,
DatabaseModule
就能被引入和配置
import { Module } from '@nestjs/common'
import { DatabaseModule } from './database/database.module'
import { User } from './users/entities/user.entity'
@Module({
imports: [DatabaseModule.forRoot([User])],
})
export class AppModule {}
如果你想要再反过来重新导出一个动态模块,你可以省略forRoot
这个方法的调用,并放在exports
数组中。
import { Module } from '@nestjs/common'
import { DatabaseModule } from './database/database.module'
import { User } from './users/entities/user.entity'
@Modules({
imports: [DatabaseModule.forRoot([User])],
exports: [DatabaseModule],
})
后记
原文地址: docs.nestjs.com/modules
关于本文
- 文章非复制黏贴,经浏览文档,以自己的理解进行,代码测试,手打书写。本篇为翻译+意译。
- 用作记录自己曾经学习、思考过的问题的一种笔记。
- 用作前端技术交流分享。
- 阅读本文时欢迎随时质疑本文的准确性,将错误的地方告诉我。本人会积极修改,避免文章对读者的误导。
关于我
- 是一只有梦想的肥柴。
- 觉得算法、数据结构、函数式编程、js底层原理等十分有趣的小前端。
- 志同道合的朋友请关注我,一起交流技术,在前端之路上共同成长。
- 如对本人有任何意见建议尽管告诉我哦~ 初为肥柴,请多多关照~
- 前端路漫漫,技术学不完。今天也是美(diao)好(fa)的一天( 跪了...orz