Techniques

模型-视图-控制器

学习如何在 NestJS 中使用 MVC 模式构建应用程序,包括 Express 和 Fastify 的模板引擎配置。

模型-视图-控制器

默认情况下,Nest 在底层使用 Express 库。因此,在 Express 中使用 MVC(模型-视图-控制器)模式的每种技术也适用于 Nest。

首先,让我们使用 CLI 工具搭建一个简单的 Nest 应用程序:

$ npm i -g @nestjs/cli
$ nest new project

为了创建一个 MVC 应用程序,我们还需要一个模板引擎来渲染我们的 HTML 视图:

$ npm install --save hbs

我们使用了 hbsHandlebars)引擎,不过您可以使用任何符合您要求的引擎。安装过程完成后,我们需要使用以下代码配置 express 实例:

@@filename(main)
import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { join } from 'path';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(
    AppModule,
  );

  app.useStaticAssets(join(__dirname, '..', 'public'));
  app.setBaseViewsDir(join(__dirname, '..', 'views'));
  app.setViewEngine('hbs');

  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();
@@switch
import { NestFactory } from '@nestjs/core';
import { join } from 'path';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(
    AppModule,
  );

  app.useStaticAssets(join(__dirname, '..', 'public'));
  app.setBaseViewsDir(join(__dirname, '..', 'views'));
  app.setViewEngine('hbs');

  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();

我们告诉 Express public 目录将用于存储静态资源,views 将包含模板,并且应该使用 hbs 模板引擎来渲染 HTML 输出。

模板渲染

现在,让我们创建一个 views 目录,并在其中创建 index.hbs 模板。在模板中,我们将打印从控制器传递的 message

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>App</title>
  </head>
  <body>
    {{ "{{ message }\}" }}
  </body>
</html>

接下来,打开 app.controller 文件,并用以下代码替换 root() 方法:

@@filename(app.controller)
import { Get, Controller, Render } from '@nestjs/common';

@Controller()
export class AppController {
  @Get()
  @Render('index')
  root() {
    return { message: 'Hello world!' };
  }
}

在这段代码中,我们在 @Render() 装饰器中指定要使用的模板,路由处理程序方法的返回值被传递给模板进行渲染。请注意,返回值是一个具有 message 属性的对象,与我们在模板中创建的 message 占位符匹配。

当应用程序运行时,打开您的浏览器并导航到 http://localhost:3000。您应该看到 Hello world! 消息。

动态模板渲染

如果应用程序逻辑必须动态决定要渲染哪个模板,那么我们应该使用 @Res() 装饰器,并在我们的路由处理程序中提供视图名称,而不是在 @Render() 装饰器中:

提示 当 Nest 检测到 @Res() 装饰器时,它会注入特定于库的 response 对象。我们可以使用这个对象来动态渲染模板。在这里了解更多关于 response 对象 API 的信息。

@@filename(app.controller)
import { Get, Controller, Res, Render } from '@nestjs/common';
import { Response } from 'express';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private appService: AppService) {}

  @Get()
  root(@Res() res: Response) {
    return res.render(
      this.appService.getViewName(),
      { message: 'Hello world!' },
    );
  }
}

示例

一个工作示例可在这里获得。

Fastify

如本章节中所述,我们能够将任何兼容的 HTTP 提供者与 Nest 一起使用。其中一个库是 Fastify。为了使用 Fastify 创建 MVC 应用程序,我们必须安装以下包:

$ npm i --save @fastify/static @fastify/view handlebars

接下来的步骤涵盖了与 Express 使用的几乎相同的过程,只有特定于平台的细微差别。安装过程完成后,打开 main.ts 文件并更新其内容:

@@filename(main)
import { NestFactory } from '@nestjs/core';
import { NestFastifyApplication, FastifyAdapter } from '@nestjs/platform-fastify';
import { AppModule } from './app.module';
import { join } from 'path';

async function bootstrap() {
  const app = await NestFactory.create<NestFastifyApplication>(
    AppModule,
    new FastifyAdapter(),
  );
  app.useStaticAssets({
    root: join(__dirname, '..', 'public'),
    prefix: '/public/',
  });
  app.setViewEngine({
    engine: {
      handlebars: require('handlebars'),
    },
    templates: join(__dirname, '..', 'views'),
  });
  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();
@@switch
import { NestFactory } from '@nestjs/core';
import { FastifyAdapter } from '@nestjs/platform-fastify';
import { AppModule } from './app.module';
import { join } from 'path';

async function bootstrap() {
  const app = await NestFactory.create(AppModule, new FastifyAdapter());
  app.useStaticAssets({
    root: join(__dirname, '..', 'public'),
    prefix: '/public/',
  });
  app.setViewEngine({
    engine: {
      handlebars: require('handlebars'),
    },
    templates: join(__dirname, '..', 'views'),
  });
  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();

Fastify API 有一些差异,但这些方法调用的最终结果是相同的。一个值得注意的差异是,当使用 Fastify 时,您传递给 @Render() 装饰器的模板名称必须包含文件扩展名。

以下是您可以设置它的方法:

@@filename(app.controller)
import { Get, Controller, Render } from '@nestjs/common';

@Controller()
export class AppController {
  @Get()
  @Render('index.hbs')
  root() {
    return { message: 'Hello world!' };
  }
}

或者,您可以使用 @Res() 装饰器直接注入响应并指定要渲染的视图,如下所示:

import { Res } from '@nestjs/common';
import { FastifyReply } from 'fastify';

@Get()
root(@Res() res: FastifyReply) {
  return res.view('index.hbs', { title: 'Hello world!' });
}

当应用程序运行时,打开您的浏览器并导航到 http://localhost:3000。您应该看到 Hello world! 消息。

示例

一个工作示例可在这里获得。