懒加载模块
懒加载模块
默认情况下,模块是急切加载的,这意味着一旦应用程序加载,所有模块也会加载,无论它们是否立即需要。虽然这对大多数应用程序来说都很好,但对于在无服务器环境中运行的应用程序/工作进程来说,这可能会成为瓶颈,其中启动延迟("冷启动")至关重要。
懒加载可以通过仅加载特定无服务器函数调用所需的模块来帮助减少引导时间。此外,一旦无服务器函数"预热",您还可以异步加载其他模块,以进一步加快后续调用的引导时间(延迟模块注册)。
提示 如果您熟悉 Angular 框架,您可能之前见过"懒加载模块"这个术语。请注意,这种技术在 Nest 中功能上是不同的,因此请将其视为一个完全不同的功能,只是共享相似的命名约定。
警告 请注意,生命周期钩子方法不会在懒加载模块和服务中调用。
入门
要按需加载模块,Nest 提供了 LazyModuleLoader 类,可以通过正常方式注入到类中:
@@filename(cats.service)
@Injectable()
export class CatsService {
constructor(private lazyModuleLoader: LazyModuleLoader) {}
}
@@switch
@Injectable()
@Dependencies(LazyModuleLoader)
export class CatsService {
constructor(lazyModuleLoader) {
this.lazyModuleLoader = lazyModuleLoader;
}
}
提示 LazyModuleLoader 类从 @nestjs/core 包导入。
或者,您可以从应用程序引导文件(main.ts)中获取对 LazyModuleLoader 提供者的引用,如下所示:
// "app" 表示 Nest 应用程序实例
const lazyModuleLoader = app.get(LazyModuleLoader);
有了这个,您现在可以使用以下构造加载任何模块:
const { LazyModule } = await import('./lazy.module');
const moduleRef = await this.lazyModuleLoader.load(() => LazyModule);
提示 "懒加载"模块在第一次 LazyModuleLoader#load 方法调用时被缓存。这意味着,每次连续尝试加载 LazyModule 都将非常快,并将返回缓存的实例,而不是再次加载模块。
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:
@Module({
providers: [LazyService],
exports: [LazyService],
})
export class LazyModule {}
提示 懒加载模块不能注册为全局模块,因为这根本没有意义(因为它们是懒加载的,按需注册,当所有静态注册的模块都已经实例化时)。同样,注册的全局增强器(守卫/拦截器等)也不会正常工作。
有了这个,我们可以获得对 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,请确保更新您的 tsconfig.json 文件 - 将 compilerOptions.module 设置为 "esnext" 并添加 compilerOptions.moduleResolution 属性,值为 "node":
{
"compilerOptions": {
"module": "esnext",
"moduleResolution": "node",
...
}
}
设置这些选项后,您将能够利用代码分割功能。
懒加载控制器、网关和解析器
由于 Nest 中的控制器(或 GraphQL 应用程序中的解析器)表示路由/路径/主题集合(或查询/变更),您不能使用 LazyModuleLoader 类懒加载它们。
警告 在懒加载模块内注册的控制器、解析器和网关不会按预期行为。同样,您不能按需注册中间件函数(通过实现 MiddlewareConsumer 接口)。
例如,假设您正在构建一个 REST API(HTTP 应用程序),底层使用 Fastify 驱动程序(使用 @nestjs/platform-fastify 包)。Fastify 不允许在应用程序准备好/成功监听消息后注册路由。这意味着即使我们分析了模块控制器中注册的路由映射,所有懒加载的路由都无法访问,因为无法在运行时注册它们。
同样,我们作为 @nestjs/microservices 包的一部分提供的一些传输策略(包括 Kafka、gRPC 或 RabbitMQ)需要在建立连接之前订阅/监听特定主题/通道。一旦您的应用程序开始监听消息,框架将无法订阅/监听新主题。
最后,启用代码优先方法的 @nestjs/graphql 包会根据元数据自动生成 GraphQL 模式。这意味着,它需要预先加载所有类。否则,将无法创建适当的有效模式。
常见用例
最常见的情况是,当您的工作进程/cron 作业/lambda 和无服务器函数/webhook 必须根据输入参数(路由路径/日期/查询参数等)触发不同的服务(不同的逻辑)时,您会看到懒加载模块。另一方面,对于单体应用程序,懒加载模块可能没有太大意义,因为启动时间相对无关紧要。