Nest 生命周期

是什么
针对前端 coder,对生命周期不陌生,无论是 React 还是 Vue,都存在这样的概念。
其实,我对生命周期的简单理解:生命周期就是一个回调函数,当程序被执行到某一时刻时,该回调函数被唤起,被执行。
同理,针对 nest 程序也是一样的道理。nest 程序启动时,会递归解析 module 依赖,扫描其中的 provider、controller,注入它的依赖。全部解析完后,会监听网络端口,开始处理请求。那么在这个过程中,nest 暴露一些函数钩子(也就是生命周期),当在解析的过程中就可以执行生命周期事件,来进行逻辑处理(比如说初始化一些数据)。
那么 nest 暴露出了哪些生命周期函数呢?

这是 nest 程序执行顺序的流程图。其中颜色加深(背景为黑色)的,就是 nest 暴露出来的钩子函数,解析过程中按照上面的顺序依次执行的。
onModuleInit
:模块被初始化后触发。onApplicationBootstrap
:所有的模块都已初始化后触发。onModuleDestroy
:模块被销毁前触发,做清理工作。beforeApplicationShutdown
:程序关闭之前,做清理工作,通常在应用程序的根模块或者全局服务中使用。onApplicationShutdown
:当应用程序准备关闭时触发,做清理工作。
onModuleDestroy 和 beforeApplicationShutdown 是不是没区别呢?
其实是有的,通过 ts 类型定义可以看出来
export interface OnModuleDestroy { onModuleDestroy(): any; } export interface BeforeApplicationShutdown { beforeApplicationShutdown(signal?: string): any; }
就是 beforeApplicationShutdown 可以拿到一个系统信号 signal,可以进行信号做一些逻辑操作。
还有注意的就是:
上面暴露出来的所有钩子函数,在 module、controller、provider 都是存在的,也就是都可以使用。那么它们之间的差异就在于执行顺序:
先执行 controller 和 provider 的方法,前面的被执行完成之后,再才会去执行 module 的方法
。
对生命周期的概念和以及 nest 暴露出来的钩子函数,都有所了解吧。
何时使用
了解 nestjs 的生命周期之后,就需要知道在每个生命周期里面,应该做什么:
onModuleInit
: 模块加载完成后立即执行某些初始化逻辑,如数据库连接、配置服务、启动定时任务等。OnApplicationBootstrap
:应用程序完全启动,并且开始准备接收客户端发送的请求,做一些全局的初始化逻辑,如设置全局中间件,定义全局状态,监听应用事件等。onModuleDestroy
:当模块被销毁时,使用这个钩子来执行模块级别的清理工作,比如关闭模块特有的服务或资源。beforeApplicationShutdown
|OnApplicationShutdown
:应用程序关闭之前,你可能需要执行一些全局的清理工作,如断开数据库, 停止所有定时任务,初始化全局状态等。
后续发现其他的应用场景,继续补充。
怎么用
每个生命周期钩子都由一个接口表示。 接口在技术上是可选的,因为它们在 TypeScript 编译后不存在。 尽管如此,使用它们以从强类型和编辑器工具中获益是一种很好的做法。
要注册生命周期钩子,请实现适当的接口。
从 @nestjs/common
导入五种生命周期接口,然后进行类实现
import {
OnModuleInit,
OnApplicationBootstrap,
OnModuleDestroy,
BeforeApplicationShutdown,
OnApplicationShutdown,
} from '@nestjs/common';
// 类实现(五种生命周期)
export class AppModule
implements
OnModuleInit,
OnApplicationBootstrap,
OnModuleDestroy,
BeforeApplicationShutdown,
OnApplicationShutdown
{
onModuleInit() {},
onApplicationBootstrap() {},
onModuleDestroy() {},
beforeApplicationShutdown() {},
onApplicationShutdown() {}
}
接下来,验证执行顺序。
首页先创建两个模块
nest g res aaa # 创建 aaa 模块
nest g res bbb # 创建 bbb 模块
然后在 AppModule
中进入导入。

分别在 aaa
、bbb
、 app
三个模块中,针对 module、controller、service 中都实现生命周期接口。
初始化阶段以及运行阶段
// app.module.ts
@Module()
export class AppModule implements OnModuleInit, OnApplicationBootstrap {
onModuleInit() {
console.log('app module onModuleInit');
}
onApplicationBootstrap(): any {
console.log('app module onApplicationBootstrap');
}
}
// app.controller.ts
@Controller()
export class AppController implements OnModuleInit, OnApplicationBootstrap {
constructor(private readonly appService: AppService) {}
onModuleInit() {
console.log('app controller onModuleInit');
}
onApplicationBootstrap(): any {
console.log('app controller onApplicationBootstrap');
}
}
// app.service.ts
@Injectable()
export class AppService implements OnModuleInit, OnApplicationBootstrap {
onModuleInit() {
console.log('app service onModuleInit');
}
onApplicationBootstrap(): any {
console.log('app service onApplicationBootstrap');
}
}
aaa、bbb 模块也是一样的,只是打印的内容有所变动。

- 先执行
onModuleInit
,再执行onApplicationBootstrap
。 - 先执行子模块中的生命周期,在执行主模块的生命周期(针对子模块而言,谁先解析谁先执行)。
- 针对模块而言,先执行 controller 和 service 的生命周期,在执行 module 的生命周期。
卸载阶段
// app.module.ts
export class AppModule
implements OnModuleDestroy, BeforeApplicationShutdown, OnApplicationShutdown
{
onModuleDestroy() {
console.log('app module onModuleDestroy');
}
beforeApplicationShutdown(signal?: string): any {
console.log('app module beforeApplicationShutdown');
}
onApplicationShutdown(signal?: string): any {
console.log('app module onApplicationShutdown');
}
}
// app.controller.ts
export class AppController
implements OnModuleDestroy, BeforeApplicationShutdown, OnApplicationShutdown
{
constructor(private readonly appService: AppService) {}
onModuleDestroy() {
console.log('app controller onModuleDestroy');
}
beforeApplicationShutdown(signal?: string): any {
console.log('app controller beforeApplicationShutdown');
}
onApplicationShutdown(signal?: string): any {
console.log('app controller onApplicationShutdown');
}
}
// app.service.ts
export class AppService
implements OnModuleDestroy, BeforeApplicationShutdown, OnApplicationShutdown
{
onModuleDestroy() {
console.log('app service onModuleDestroy');
}
beforeApplicationShutdown(signal?: string): any {
console.log('app service beforeApplicationShutdown');
}
onApplicationShutdown(signal?: string): any {
console.log('app service onApplicationShutdown');
}
}
然后再 main.ts 中执行 app.close()
操作(app.close() 只是触发销毁逻辑,但不会真正退出进程)
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
setTimeout(() => {
app.close();
}, 3000);
}
bootstrap();
3 秒过后就会打印出

- 生命周期的执行顺序 onModuleDestroy > beforeApplicationShutdown > onApplicationShutdown。
而且所有的生命周期函数都是支持 async 的。
在卸载的生命周期中,拿到当前模块的引用 moduleRef
,调用 get 方法,传入 token,取出对应的 provider 实例,然后调用它的方法(目的为了做卸载操作的逻辑)
// aaa.module.ts
import { Module, OnApplicationShutdown } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core';
import { AaaService } from './aaa.service';
import { AaaController } from './aaa.controller';
@Module({
providers: [AaaService],
controllers: [AaaController],
})
export class AaaModule implements OnApplicationShutdown {
constructor(private moduleRef: ModuleRef) {}
onApplicationShutdown(signal?: string): any {
// 拿到实例,调用其中的方法
const aaaService = this.moduleRef.get<AaaService>(AaaService);
console.log('========================', aaaService.findAll());
console.log('aaa module onApplicationShutdown');
}
}
这就是 nest 生命周期的大致用法。
总结
- 生命周期就是在解析过程中,在某一刻执行的回调钩子函数。
- 生命周期的实现是通过接口实现(
implements
)的,在 nestjs 内部提供了五种生命周期接口。 - 初始阶段(onModuleInit,onApplicationBootstrap),挂载(onModuleDestroy, beforeApplicationShutdown, onApplicationShutdown)
- 生命周期中做着不同是事情,需要自己去收集总结。
- 在挂载阶段中,拿取 provide 实例是通过 moduleRef 来进行拿取的
- 每个生命周期钩子都是支持异步的 async。
转载自:https://juejin.cn/post/7379826188749586473