NestJS 延遲加載模塊

2023-09-08 14:52 更新

默認(rèn)情況下,模塊是預(yù)先加載的,這意味著一旦應(yīng)用加載,所有模塊也會(huì)加載,無(wú)論它們是否立即需要。 雖然這對(duì)大多數(shù)應(yīng)用來(lái)說(shuō)都很好,但它可能成為在 無(wú)服務(wù)器環(huán)境 中運(yùn)行的應(yīng)用/工作程序的瓶頸,其中啟動(dòng)延遲 ("冷啟動(dòng)") 至關(guān)重要。

延遲加載可以通過(guò)僅加載特定無(wú)服務(wù)器函數(shù)調(diào)用所需的模塊來(lái)幫助減少引導(dǎo)時(shí)間。 此外,你還可以在無(wú)服務(wù)器功能為 "warm" 時(shí)異步加載其他模塊,以進(jìn)一步加快后續(xù)調(diào)用的引導(dǎo)時(shí)間(延遲模塊注冊(cè))。

提示如果你熟悉 Angular 框架,你可能以前見(jiàn)過(guò) "延遲加載模塊" 術(shù)語(yǔ)。 請(qǐng)注意,此技術(shù)是 Nest 中的 功能不同,因此請(qǐng)將其視為具有相似命名約定的完全不同的功能。
警告請(qǐng)注意,生命周期鉤子方法 不會(huì)在延遲加載的模塊和服務(wù)中調(diào)用。

入門(mén)

為了按需加載模塊,Nest 提供了 LazyModuleLoader 類,可以以正常方式注入到類中:

cats.service.ts

@Injectable()
export class CatsService {
  constructor(private lazyModuleLoader: LazyModuleLoader) {}
}
提示LazyModuleLoader 類是從 @nestjs/core 包中導(dǎo)入的。

或者,你可以從應(yīng)用引導(dǎo)程序文件 (main.ts) 中獲取對(duì) LazyModuleLoader 提供程序的引用,如下所示:

// "app" represents a Nest application instance
const lazyModuleLoader = app.get(LazyModuleLoader);

有了這個(gè),你現(xiàn)在可以使用以下結(jié)構(gòu)加載任何模塊:

const { LazyModule } = await import('./lazy.module');
const moduleRef = await this.lazyModuleLoader.load(() => LazyModule);
提示"Lazy-loaded" 模塊在第一個(gè) LazyModuleLoader#load 方法調(diào)用時(shí)是 cached。 這意味著,每次連續(xù)嘗試加載 LazyModule 都將是 非???,并將返回一個(gè)緩存實(shí)例,而不是再次加載模塊。 Load "LazyModule" attempt: 1 time: 2.379ms Load "LazyModule" attempt: 2 time: 0.294ms Load "LazyModule" attempt: 3 time: 0.303ms 此外,"lazy-loaded" 模塊與那些在應(yīng)用引導(dǎo)程序中預(yù)加載的模塊以及稍后在你的應(yīng)用中注冊(cè)的任何其他惰性模塊共享相同的模塊圖。

其中 lazy.module.ts 是導(dǎo)出 常規(guī) Nest 模塊 的 TypeScript 文件(不需要額外更改)。

LazyModuleLoader#load 方法返回 模塊參考(屬于 LazyModule),它允許你導(dǎo)航內(nèi)部提供者列表并使用其注入令牌作為查找鍵獲取對(duì)任何提供者的引用。

例如,假設(shè)我們有一個(gè)具有以下定義的 LazyModule:

@Module({
  providers: [LazyService],
  exports: [LazyService],
})
export class LazyModule {}
提示延遲加載的模塊不能注冊(cè)為 全局模塊,因?yàn)樗緵](méi)有意義(因?yàn)樗鼈兪茄舆t注冊(cè)的,當(dāng)所有靜態(tài)注冊(cè)的模塊已經(jīng)實(shí)例化時(shí)按需注冊(cè))。 同樣,正確注冊(cè) 全局增強(qiáng)子(守衛(wèi)/攔截器等)不管用。

有了這個(gè),我們可以獲得對(duì) LazyService 提供者的引用,如下所示:

const { LazyModule } = await import('./lazy.module');
const moduleRef = await this.lazyModuleLoader.load(() => LazyModule);

const { LazyService } = await import('./lazy.service');
const lazyService = moduleRef.get(LazyService);
警告如果你使用 Webpack,請(qǐng)確保更新你的 tsconfig.json 文件 - 將 compilerOptions.module 設(shè)置為 "esnext" 并添加 compilerOptions.moduleResolution 屬性并將 "node" 作為值: { "compilerOptions": { "module": "esnext", "moduleResolution": "node", ... } } 設(shè)置這些選項(xiàng)后,你將能夠利用 代碼拆分 功能。

延遲加載控制器、網(wǎng)關(guān)和解析器

由于 Nest 中的控制器(或 GraphQL 應(yīng)用中的解析器)表示路由/路徑/主題(或查詢/突變)的集合,因此你 不能延遲加載它們 使用 LazyModuleLoader 類。

警告在延遲加載模塊中注冊(cè)的控制器、resolvers 和 gateways 將不會(huì)按預(yù)期運(yùn)行。 同樣,你不能按需注冊(cè)中間件函數(shù)(通過(guò)實(shí)現(xiàn) MiddlewareConsumer 接口)。

例如,假設(shè)你正在使用底層的 Fastify 驅(qū)動(dòng)程序(使用 @nestjs/platform-fastify 包)構(gòu)建一個(gè) REST API(HTTP 應(yīng)用)。 Fastify 不允許你在應(yīng)用準(zhǔn)備好/成功監(jiān)聽(tīng)消息后注冊(cè)路由。 這意味著即使我們分析了在模塊控制器中注冊(cè)的路由映射,也無(wú)法訪問(wèn)所有延遲加載的路由,因?yàn)闊o(wú)法在運(yùn)行時(shí)注冊(cè)它們。

同樣,我們作為 @nestjs/microservices 包的一部分提供的一些傳輸策略(包括 Kafka、gRPC 或 RabbitMQ)需要在建立連接之前訂閱/收聽(tīng)特定主題/通道。 一旦你的應(yīng)用開(kāi)始收聽(tīng)消息,框架將無(wú)法訂閱/收聽(tīng)新主題。

最后,啟用了代碼優(yōu)先方法的 @nestjs/graphql 包會(huì)根據(jù)元數(shù)據(jù)自動(dòng)即時(shí)生成 GraphQL 模式。 這意味著,它需要預(yù)先加載所有類。 否則,將無(wú)法創(chuàng)建適當(dāng)?shù)?、有效的模式?/p>

常見(jiàn)用例

最常見(jiàn)的情況是,當(dāng)你的 worker/cron 作業(yè)/lambda 和無(wú)服務(wù)器函數(shù)/webhook 必須根據(jù)輸入?yún)?shù)(路由路徑/日期/查詢參數(shù)等)觸發(fā)不同的服務(wù)(不同的邏輯)時(shí),你會(huì)看到延遲加載的模塊。 另一方面,延遲加載模塊對(duì)于整體應(yīng)用可能沒(méi)有太大意義,因?yàn)閱?dòng)時(shí)間無(wú)關(guān)緊要。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)