延迟加载模块
默认情况下,模块是急切加载的,这意味着一旦应用程序加载,所有模块都会加载,无论它们是否立即需要。 虽然这对大多数应用程序来说没问题,但对于在无服务器环境中运行的应用程序/工作程序, 启动延迟(“冷启动”)至关重要时,这可能成为一个瓶颈。
懒加载可以通过仅加载特定无服务器函数调用所需的模块来帮助减少引导时间。 此外,您还可以在服务 器函数“热”后异步加载其他模块,以进一步加快后续调用的引导时间(延迟模块注册)。
如果您熟悉Angular框架,您可能之前见过“懒加载模块”的术语。 请注意,这个技术在Nest中的功能与之前不同,因此请将其视为一种完全不同的功能,尽管它共享相似的命名约定。
请注意,懒加载模块和服务不会调用生命周期钩子方法。
入门
为了按需加载模块,Nest提供了可以以正常方式注入到类中的LazyModuleLoader
类:
@Injectable()
export class CatsService {
constructor(private 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
并添加以node
为值的
compilerOptions.moduleResolution
属性。
{
"compilerOptions": {
"module": "esnext",
"moduleResolution": "node",
...
}
}
设置这些选项后,您将能够利用代码分割功能。
懒加载控制器、网关和解析器
由于在Nest中,控制器(或在GraphQL应用程序中的解析器)表示一组路由/路径/主题(或查询/变更),
您不能使用LazyModuleLoader
类对它们进行懒加载。
在懒加载模块中注册的控制器、解析器和网关将不会按预期行为。
同样,您不能按需注册中间件函数(通过实现MiddlewareConsumer
接口)。
例如,假设您正在使用Fastify驱动程序(使用@nestjs/platform-fastify
包)在底层构建REST API(HTTP应用程序)。
Fastify不允许在应用程序准备就绪/成功监听消息后注册路由。
这意味着即使我们分析了模块控制器中注册的路由映射,由于没有办法在运行时注册它们,所有懒加载的路由都将无法访问。
同样,我们作为@nestjs/microservices
包的一部分提供的一些传输策略
(包括Kafka、gRPC或RabbitMQ)要求在建立连接之前订阅/监听特定的主题/通道。
一旦您的应用程序开始监听消息,框架将无法订阅/监听新主题。
最后,@nestjs/graphql
包启用代码优先方法,会根据元数据动态生成GraphQL模式。
这意味着它需要预先加载所有类。否则,将无法创建适当的有效模式。
常见用例
在大多数情况下,您会在以下情况下看到懒加载模块: 当您的工作程序/定时作业/lambda和无服务器函数/webhook必须基于输入参数(路由路径/日期/查询参数等)触发不同服务(不同逻辑)时。 另一方面,在单体应用程序中,启动时间不太重要时,懒加载模块可能没有太多意义。