Skip to content

懒加载模块

默认情况下,模块是急切加载的,这意味着应用程序一加载,所有模块也会随之加载,无论它们是否立即需要。虽然这对大多数应用程序来说没有问题,但对于在无服务器环境中运行的应用/工作进程来说,启动延迟("冷启动")至关重要,这可能会成为瓶颈。

懒加载可以通过仅加载特定无服务器函数调用所需的模块来帮助减少启动时间。此外,你还可以在无服务器函数"预热"后异步加载其他模块,以进一步加快后续调用的启动时间(延迟模块注册)。

提示

如果你熟悉 Angular 框架,你可能之前见过"懒加载模块"这个术语。请注意,这项技术在 Nest 中功能上是不同的,因此请将其视为一个完全不同的特性,只是共享了类似的命名约定。

警告

请注意,生命周期钩子方法不会在懒加载的模块和服务中被调用。

入门

要按需加载模块,Nest 提供了 LazyModuleLoader 类,可以通过常规方式注入到类中:

typescript
@Injectable()
export class CatsService {
  constructor(private lazyModuleLoader: LazyModuleLoader) {}
}

提示

LazyModuleLoader 类从 @nestjs/core 包中导入。

或者,你可以从应用程序引导文件(main.ts)中获取 LazyModuleLoader 提供者的引用,如下所示:

typescript
// "app" 代表一个 Nest 应用程序实例
const lazyModuleLoader = app.get(LazyModuleLoader);

有了这些,你现在可以使用以下方式加载任何模块:

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

提示

"懒加载"模块在第一次调用 LazyModuleLoader#load 方法时会被缓存。这意味着,每次后续加载 LazyModule 的尝试都会非常快,并返回一个缓存的实例,而不是重新加载模块。

bash
Load "LazyModule" attempt: 1
time: 2.379ms
Load "LazyModule" attempt: 2
time: 0.294ms
Load "LazyModule" attempt: 3
time: 0.303ms

此外,"懒加载"模块与应用程序启动时急切加载的模块以及之后在应用中注册的任何其他懒加载模块共享相同的模块图。

其中 lazy.module.ts 是一个导出常规 Nest 模块的 TypeScript 文件(不需要额外的更改)。

LazyModuleLoader#load 方法返回 LazyModule模块引用,它允许你浏览内部提供者列表,并使用注入令牌作为查找键来获取任何提供者的引用。

例如,假设我们有一个具有以下定义的 LazyModule

typescript
@Module({
  providers: [LazyService],
  exports: [LazyService],
})
export class LazyModule {}

提示

懒加载模块不能注册为全局模块,因为这没有意义(因为它们是懒加载的,按需注册的,此时所有静态注册的模块已经被实例化了)。同样,已注册的全局增强器(守卫/拦截器等)也无法正常工作

有了这些,我们可以按如下方式获取 LazyService 提供者的引用:

typescript
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,请确保更新你的 tsconfig.json 文件——将 compilerOptions.module 设置为 "esnext",并添加值为 "node"compilerOptions.moduleResolution 属性:

json
{
  "compilerOptions": {
    "module": "esnext",
    "moduleResolution": "node",
    ...
  }
}

设置好这些选项后,你就可以利用代码分割功能了。

懒加载控制器、网关和解析器

由于 Nest 中的控制器(或 GraphQL 应用程序中的解析器)代表路由/路径/主题(或查询/变更)的集合,你不能使用 LazyModuleLoader 类来懒加载它们。

error 警告 在懒加载模块中注册的控制器、解析器网关不会按预期工作。同样,你也不能按需注册中间件函数(通过实现 MiddlewareConsumer 接口)。

例如,假设你正在使用 Fastify 驱动(使用 @nestjs/platform-fastify 包)构建一个 REST API(HTTP 应用程序)。Fastify 不允许在应用程序准备就绪/成功监听消息后注册路由。这意味着即使我们分析了模块控制器中注册的路由映射,所有懒加载的路由也将无法访问,因为没有办法在运行时注册它们。

同样,我们作为 @nestjs/microservices 包的一部分提供的某些传输策略(包括 Kafka、gRPC 或 RabbitMQ)需要在连接建立之前订阅/监听特定的主题/通道。一旦你的应用程序开始监听消息,框架将无法订阅/监听新的主题。

最后,启用了代码优先方式的 @nestjs/graphql 包会根据元数据自动动态生成 GraphQL 模式。这意味着它需要预先加载所有类。否则,将无法创建适当的、有效的模式。

常见用例

最常见的情况是,当你的工作进程/定时任务/Lambda 和无服务器函数/Webhook 必须根据输入参数(路由路径/日期/查询参数等)触发不同的服务(不同的逻辑)时,你会看到懒加载模块的使用。另一方面,对于单体应用程序来说,懒加载模块可能没有太大意义,因为启动时间在这种情况下并不那么重要。

基于 NestJS 官方文档翻译