## 注入作用域
來自不同語言背景的開發者,在學習Nest時可能預料不到在請求中幾乎所有內容都是共享的。我們建立一個連接池到數據庫,在全局狀態下使用單例服務。 要記住Node.js并不遵循多線程下請求/響應的無狀態模式。因此,在我們的應用中使用單例是安全的。
然而,在需要考慮請求生命周期的情況下,存在邊緣情況.例如,在GraphQL應用的預請求緩存中,以及請求追蹤和多租戶條件下,注入作用域提供了一個機制來獲取需要的提供者生命周期行為.
### 提供者范圍
基本上,每個提供者都可以作為一個單例,被請求范圍限定,并切換到瞬態模式。請參見下表,以熟悉它們之間的區別。
|||
|---|----|
| `DEFAULT` | 每個提供者可以跨多個類共享。提供者生命周期嚴格綁定到應用程序生命周期。一旦應用程序啟動,所有提供程序都已實例化。默認情況下使用單例范圍。 |
| `REQUEST` | 在請求處理完成后,將為每個傳入請求和垃圾收集專門創建提供者的新實例 |
| `TRANSIENT` | 臨時提供者不能在提供者之間共享。每當其他提供者向 `Nest` 容器請求特定的臨時提供者時,該容器將創建一個新的專用實例 |
> 對于大多數用例,**建議**使用單例范圍。請求之間共享提供者可以降低內存消耗,從而提高應用程序的性能(不需要每次實例化類)。
### 使用 (Usage)
為了切換到另一個注入范圍,您必須向 `@Injectable()` 裝飾器傳遞一個選項對象。
```typescript
import { Injectable, Scope } from '@nestjs/common';
@Injectable({ scope: Scope.REQUEST })
export class CatsService {}
```
在[自定義提供者](https://docs.nestjs.com/fundamentals/custom-providers)的情況下,您必須設置一個額外的范圍屬性。
```typescript
{
provide: 'CACHE_MANAGER',
useClass: CacheManager,
scope: Scope.TRANSIENT,
}
```
> `Scope`從`@nestjs/common`中導入。
> 網關不應該使用請求范圍提供者,因為其必須作為單例提供。每個網關都封裝了一個`socket`并且不能多次實例化。
默認使用單例范圍,并且不需要聲明。如果你想聲明一個單例范圍的提供者,在`scope`屬性中使用`Scope.DEFAULT`值。
### 控制器范圍
控制器也可以有范圍,它適用于在該控制器中聲明的所有請求方法處理程序。與提供者作用域一樣,控制器的作用域聲明了它的生命周期。對于請求范圍的控制器,為每個入站請求創建一個新實例,并在請求完成處理時進行垃圾收集。
`scope`使用對象的屬性聲明控制器范圍`ControllerOptions`:
~~~typescript
@Controller({
path: 'cats',
scope: Scope.REQUEST,
})
export class CatsController {}
~~~
> 網關永遠不應該依賴于請求范圍的提供者,因為它們充當單例。一個網關封裝了一個真正的套接字,不能多次被實例化
### 所有請求注入
必須非常謹慎地使用請求范圍的提供者。請記住,`scope` 實際上是在注入鏈中冒泡的。如果您的控制器依賴于一個請求范圍的提供者,這意味著您的控制器實際上也是請求范圍。
想象一下下面的鏈: `CatsController <- CatsService <- CatsRepository `。如果您的 `CatsService` 是請求范圍的(從理論上講,其余的都是單例),那么 `CatsController` 也將成為請求范圍的(因為必須將請求范圍的實例注入到新創建的控制器中),而 `CatsRepository` 仍然是單例的。因為它依賴于注入的服務。不依賴的`CatsRepository`將保持單例范圍。
> 在這種情況下,循環依賴關系將導致非常痛苦的副作用,因此,您當然應該避免創建它們。
### 請求提供者
在 `HTTP` 應用程序中(例如使用`@nestjs/platform-express`或`@nestjs/platform-fastify`),當使用請求范圍提供者時,可能需要獲取原始的請求對象。您可以通過注入`REQUEST`對象來做到這一點。
```typescript
import { Injectable, Scope, Inject } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { Request } from 'express';
@Injectable({ scope: Scope.REQUEST })
export class CatsService {
constructor(@Inject(REQUEST) private readonly request: Request) {}
}
```
由于底層平臺和協議不同,該功能與微服務和 `GraphQL` 應用程序略有不同。在 `GraphQL` 應用程序中,可以注入 `CONTEXT`來替代`REQUEST`。
```typescript
import { Injectable, Scope, Inject } from '@nestjs/common';
import { CONTEXT } from '@nestjs/graphql';
@Injectable({ scope: Scope.REQUEST })
export class CatsService {
constructor(@Inject(CONTEXT) private readonly context) {}
}
```
然后,您可以配置您的 `context` 值(在`GraphQLModule`中),以包含請求作為其屬性。
### 性能
使用請求范圍的提供者將明顯影響應用程序性能。即使 `Nest` 試圖緩存盡可能多的元數據,它仍然必須為每個請求創建類的實例。因此,它將降低您的平均響應時間和總體基準測試結果。如果您的提供者不一定需要請求范圍,那么您應該堅持使用單例范圍。
- 介紹
- 概述
- 第一步
- 控制器
- 提供者
- 模塊
- 中間件
- 異常過濾器
- 管道
- 守衛
- 攔截器
- 自定義裝飾器
- 基礎知識
- 自定義提供者
- 異步提供者
- 動態模塊
- 注入作用域
- 循環依賴
- 模塊參考
- 懶加載模塊
- 應用上下文
- 生命周期事件
- 跨平臺
- 測試
- 技術
- 數據庫
- Mongo
- 配置
- 驗證
- 緩存
- 序列化
- 版本控制
- 定時任務
- 隊列
- 日志
- Cookies
- 事件
- 壓縮
- 文件上傳
- 流式處理文件
- HTTP模塊
- Session(會話)
- MVC
- 性能(Fastify)
- 服務器端事件發送
- 安全
- 認證(Authentication)
- 授權(Authorization)
- 加密和散列
- Helmet
- CORS(跨域請求)
- CSRF保護
- 限速
- GraphQL
- 快速開始
- 解析器(resolvers)
- 變更(Mutations)
- 訂閱(Subscriptions)
- 標量(Scalars)
- 指令(directives)
- 接口(Interfaces)
- 聯合類型
- 枚舉(Enums)
- 字段中間件
- 映射類型
- 插件
- 復雜性
- 擴展
- CLI插件
- 生成SDL
- 其他功能
- 聯合服務
- 遷移指南
- Websocket
- 網關
- 異常過濾器
- 管道
- 守衛
- 攔截器
- 適配器
- 微服務
- 概述
- Redis
- MQTT
- NATS
- RabbitMQ
- Kafka
- gRPC
- 自定義傳輸器
- 異常過濾器
- 管道
- 守衛
- 攔截器
- 獨立應用
- Cli
- 概述
- 工作空間
- 庫
- 用法
- 腳本
- Openapi
- 介紹
- 類型和參數
- 操作
- 安全
- 映射類型
- 裝飾器
- CLI插件
- 其他特性
- 遷移指南
- 秘籍
- CRUD 生成器
- 熱重載
- MikroORM
- TypeORM
- Mongoose
- 序列化
- 路由模塊
- Swagger
- 健康檢查
- CQRS
- 文檔
- Prisma
- 靜態服務
- Nest Commander
- 問答
- Serverless
- HTTP 適配器
- 全局路由前綴
- 混合應用
- HTTPS 和多服務器
- 請求生命周期
- 常見錯誤
- 實例
- 遷移指南
- 發現
- 誰在使用Nest?