likes
comments
collection
share

Nest 生命周期

作者站长头像
站长
· 阅读数 51
Nest 生命周期

是什么

针对前端 coder,对生命周期不陌生,无论是 React 还是 Vue,都存在这样的概念。

其实,我对生命周期的简单理解:生命周期就是一个回调函数,当程序被执行到某一时刻时,该回调函数被唤起,被执行

同理,针对 nest 程序也是一样的道理。nest 程序启动时,会递归解析 module 依赖,扫描其中的 provider、controller,注入它的依赖。全部解析完后,会监听网络端口,开始处理请求。那么在这个过程中,nest 暴露一些函数钩子(也就是生命周期),当在解析的过程中就可以执行生命周期事件,来进行逻辑处理(比如说初始化一些数据)。

那么 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,可以进行信号做一些逻辑操作。

还有注意的就是:

上面暴露出来的所有钩子函数,在 modulecontrollerprovider 都是存在的,也就是都可以使用。那么它们之间的差异就在于执行顺序

先执行 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 中进入导入。

Nest 生命周期

分别在 aaabbbapp 三个模块中,针对 modulecontrollerservice 中都实现生命周期接口。

初始化阶段以及运行阶段

// 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 模块也是一样的,只是打印的内容有所变动。

Nest 生命周期
  • 先执行 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 秒过后就会打印出

Nest 生命周期
  • 生命周期的执行顺序 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
评论
请登录