尽可能简化 Angular+NestJS 的单体环境设置

考虑到之前我发表的关于Angular+NestJS+OpenAPI(Swagger)的帖子,我曾思考如何将微服务纳入到这个环境中。我一直觉得NestJS和Angular的协作部分不太理想,但现在我找到了解决方案,所以我尝试以简单的单仓库结构重新构建了它。

条件

    • Node.jsインストール済み

 

    • Angular CLIインストール済み

 

    NestJSインストール済み

各种版本 (gè ɡ

$ node -v
v12.19.0
$ ng --version

     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/


Angular CLI: 10.2.0
Node: 12.19.0
OS: linux x64

Angular:
...
Ivy Workspace:

Package                      Version
------------------------------------------------------
@angular-devkit/architect    0.1002.0 (cli-only)
@angular-devkit/core         10.2.0 (cli-only)
@angular-devkit/schematics   10.2.0 (cli-only)
@schematics/angular          10.2.0 (cli-only)
@schematics/update           0.1002.0 (cli-only)
$ nest --version
7.5.1

环境建设

创建Lerna项目

Lerna是一个用于管理单一存储库的工具。
虽然没有它也没问题,但它可以对服务器和客户端项目进行批量构建和npm安装,非常方便。

# プロジェクトのディレクトリを作成して移動
mkdir mono-nestjs-angular && cd mono-nestjs-angular
# Lernaプロジェクトとして初期化
npx lerna init

使用NestJS生成模板

# 各プロジェクトはlerna initで生成されるpackagesディレクトリの下に作成する
cd packages
nest new server

Angular的模板生成

ng new client --style=scss --routing=true

设定API端的前缀

为了区分API调用和静态文件的访问,需要设置API的URL前缀以/api开头。具体步骤请参考此处。

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
    const app = await NestFactory.create(AppModule);
    // APIのURLが/apiとなるようにプレフィクスを設定
    app.setGlobalPrefix('api');

    await app.listen(3000);
}
bootstrap();

将Angular页面配置为静态文件返回。

这次我想做的是在这里。
我会参考参考文件并添加静态文件的配置。

npx lerna add @nestjs/serve-static --scope=server
# もしくは
cd server && npm install --save @nestjs/serve-static

在模块中导入ServeStaticModule。以下是此时的重点。

    • rootPathはAngularの出力ディレクトリと一致するようにします

Angularの出力ディレクトリはpackages/client/angular-cli.jsonのoutputPathに書いてあります

excludeにて上記で設定したプレフィクスを指定してAPIコール時は参照しないようにします

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ServeStaticModule } from '@nestjs/serve-static';
import { join } from 'path';

@Module({
  imports: [
    ServeStaticModule.forRoot({
        rootPath: join(__dirname, '../../client/', 'dist/client'),
        exclude: ['/api'],
      }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

添加构建和启动脚本

在项目的根目录下,通过lerna init生成的package.json文件中添加启动命令。

{
  "name": "root",
  "private": true,
  "scripts": {
      "start": "lerna run start --scope=server --stream",
      "build": "lerna run build --stream"
  },
  "devDependencies": {
    "lerna": "^3.16.1"
  }
}
    • lerna runコマンドはプロジェクト内のpackage.jsonにあるscriptsで定義されたコマンドを一括実行します

startに関してはサーバーのみ実行するため、–scopeオプションでパッケージを指定する

–streamオプションはログ出力するためのオプションです。これがないとサーバー起動してもログが出てきません。

确认操作

请在项目根目录下执行以下命令。

开动

npm install
npm run build
npm start

确认图像

访问 http://localhost:3000

image.png

你看,Angular页面正常显示出来了。

确认应用程序接口

我将尝试访问http://localhost:3000/api。

image.png

API可以调用,对吗?

能否在子目录中直接输入URL进行跳转的补充说明。

对于SPA而言,它只是通过JavaScript在index.html页面上进行页面转换,即使在URL中指定了子目录,实际上并不存在该子目录下的index.html文件,因此如果只是简单地发布静态文件,当直接在URL中输入或通过子目录按下F5刷新时,会导致404错误。
(一般情况下,复制index.html并创建404.html来应对这个问题是常见的做法吗?)

据这个页面上的注意事项,@nestjs/serve-static似乎可以很巧妙地处理这些问题,所以我想试一试。

顺便提一下,在之前写的Angular + NestJS + OpenAPI (Swagger)的环境中考虑到微服务,我们将app.use(regx, express.static(path))设置为始终返回index.html。

添加组件

添加Page1Component和Page2Component组件。

import { Component } from '@angular/core';

@Component({
    selector: 'app-page1',
    template: `
    <h1>{{title}}</h1>
    <a routerLink="/page2">to page2</a>
    `
})
export class Page1Component {
    title = 'page1';
}

※Page2Component只是把page1的部分替换成了page2的部分。

将添加的组件在module的声明中进行指定,以便能够引用。

・・・
import { Page1Component } from './pages/page1.component';
import { Page2Component } from './pages/page2.component';

@NgModule({
  declarations: [
    AppComponent,
    Page1Component,
    Page2Component,
  ],
  ・・・

进行路由设置

・・・
import { Page1Component } from './pages/page1.component';
import { Page2Component } from './pages/page2.component';

const routes: Routes = [
    { path: 'page1', component: Page1Component },
    { path: 'page2', component: Page2Component },
];
・・・

展示画面

访问 http://localhost:3000/page1

image.png

正确地显示出来了。
(Page1Component的内容已在底部输出)

image.png

页面已成功切换到Page2Component中(URL也已变成/page2了)。

总结

我可以很容易地将NestJS和Angular进行协作,比以前写的文章简单多了。毕竟NestJS受到了Angular的启发,与SPA相兼容,真是太棒了^^

我已将此次制作的内容推送到GitHub上
https://github.com/teracy55/mono-nestjs-angular

广告
将在 10 秒后关闭
bannerAds