使用【Nx+Angular+NestJS+Firebase】,在15分钟内构建并部署完整的PWA环境

大意

在重新审视构建全栈PWA环境时,我遇到了@nrwl/nx,并尝试使用它。

我能在大约15分钟的时间内完成从环境设置到部署的步骤,所以我来分享一下我的工作流程。如果能写好草图的话,可能会更快一些。

image.png

要达到目标,我们必须首先满足这个前提。

    firebase-toolsの初期設定が完了している

工作流程

    • ① Nx Workspaceの作成(3分くらい)

 

    • ② @angular/pwaのインストール(30秒くらい)

 

    • ③ Firebaseプロジェクトの作成(30秒くらい)

 

    • ④ @angular/fireのインストール(2分)

 

    • ⑤ NestJSをCloud Functions for Firebaseに対応(4分くらい)

 

    • ⑥ Firebaseのプラン変更(30秒くらい)

 

    ⑦ デプロイ(4分くらい)

创建Nx Workspace

首先,使用”create-nx-workspace”命令创建Nx的工作区。
基本上,使用npx命令进行操作。

$ npx create-nx-workspace@latest pwa-sample \
--preset angular-nest --app-name ng-pwa-sample \
--style scss --linter eslint --nx-cloud false

当PWA示例的创建工作完成后,移动到已完成的pwa-sample目录中。

$ cd pwa-sample

② 安装@angular/pwa

使用nx add来运行Schematics,就像使用ng add一样,以安装@angular/pwa以实现PWA支持。

$ npx nx add @angular/pwa

创建Firebase项目

在安装 @angular/fire 之前,请先创建一个 Firebase 项目。

$ PROJECT_NAME=sample-pwa
$ PROJECT_ID=$PROJECT_NAME-$(printf %04d $(($RANDOM % 10000)))
// sample-pwa-0021
$ npx firebase projects:create $PROJECT_ID --display-name $PROJECT_NAME

安装 @angular/fire

创建Firebase项目后,安装@angular/fire。

$ npx nx add @angular/fire

在安装时会询问要使用的Firebase项目,所以请选择之前创建的项目。

将NestJS适配到Cloud Functions for Firebase。

设置.firebaserc文件配置

在部署Cloud Functions时需要的一项是将默认项目设置为.firebaserc,可以使用以下命令进行设置。

$ npx firebase use $PROJECT_ID --alias default

安装必需的软件包

同时,安装Firebase所需的Cloud Functions包。

$ npm install firebase-functions firebase-admin express

将Cloud Functions中调用NestJS的配置。

在apps/api/src中创建main.prod.ts文件,并写入以下内容。

import { NestFactory } from '@nestjs/core';
import { ExpressAdapter } from '@nestjs/platform-express';
import * as express from 'express';
import * as functions from 'firebase-functions';
import { AppModule } from './app/app.module';

export async function bootstrap(expressInstance) {
  const app = await NestFactory.create(
    AppModule, 
    new ExpressAdapter(expressInstance)
  );
  const globalPrefix = 'api';
  app.setGlobalPrefix(globalPrefix);
  await app.init();
}

const expressServer = express();

export const api = functions.region('us-central1')
.https.onRequest(async (request, response) => {
    await bootstrap(expressServer);
    expressServer(request, response);
});

部署的配置1(编辑angular.json)

在 angular.json 中进行如下编辑:
第一项编辑是在生产环境中使用 main.prod.ts 替代 main.ts 的配置,第二项是配置 NestJS api 的部署命令。

{
  "version": 1,
  "projects": {
    ...,
    "api": {
      ...,
      "architect": {
        "build": {
          "configurations": {
            "production": {
              ...,
              "fileReplacements": [
                {
                  "replace": "apps/api/src/environments/environment.ts",
                  "with": "apps/api/src/environments/environment.prod.ts"
                },
+               {
+                 "replace": "apps/api/src/main.ts",
+                 "with": "apps/api/src/main.prod.ts"
+               }
              ]
            }
          }
        },
+       "deploy": {
+         "builder": "@nrwl/workspace:run-commands",
+         "options": {
+           "commands": [
+             {
+                 "command": "npx firebase deploy --only functions"
+             }
+           ]
+         }
+       }
      }
    }
  }
}

部署设置②(准备package.json的转换脚本)

import { readFileSync, writeFileSync } from 'fs';
import { join } from 'path';
import * as fs from 'fs';

async function main() {
    const BASE_PATH = join(__dirname, '..', '..');
    console.debug('BASE_PATH:', BASE_PATH);
    const srcDir = BASE_PATH;
    const distDir = join(BASE_PATH, 'dist/apps/api');

    const src = readFileSync(join(srcDir, 'package.json')).toString();
    const dist = convertPackageJson(src);

    if (!fs.existsSync(distDir)) fs.mkdirSync(distDir, {recursive: true});
    writeFileSync(join(distDir, 'package.json'), dist);
}

function convertPackageJson(src: string): string {
    const packageJson = JSON.parse(src);
    delete packageJson['scripts'];
    delete packageJson['devDependencies'];
    packageJson['main'] = 'main.js';
    packageJson['engines'] = {'node': '12'};
    return JSON.stringify(packageJson, null , '\t');
}

main();

准备部署配置③(准备firebase.json转换脚本)

{
  "hosting": {
     ...,
     "rewrites": [
+      {
+         "source": "/api/**",
+         "function": "api"
+      },
       {
         "source": "**",
         "destination": "/index.html"
       }
     ]
  },
+ "functions": {
+   "predeploy": [
+     "npx nx build api --prod",
+     "npx ts-node ./tools/scripts/generate-api-package-json.ts"
+   ],
+   "source": "dist/apps/api"
+ }
}

※ 重写将按照从上到下的顺序进行处理,所以要注意顺序!

第一个 hosting.rewrites 的配置是将对 https://.web.app/api/** 的请求重写为发送至 CloudFunctions 的 api 函数的请求。(示例中,https://.web.app/api/hello 会返回 NestJS 的 JSON。)
第二个 functions 是在部署 Cloud Functions 时的配置。

Firebase计划变更

要使用Cloud Functions for Firebase,需要升级到Blaze计划,并在Firebase控制台的“Functions”选项卡中进行启用。

Firebase 控制台

⑦ 部署

使用nx命令,根据angular.json中的配置进行部署。

// Deploy NestJS App
$ npx nx deploy api
// Deploy Angular App
$ npx nx deploy ng-pwa-sample 

从浏览器进行确认。

当部署完成后,Firebase Hosting的网址会显示出来,访问并确认。
若在”Message”部分显示{“message”: “Welcome to api!”},则表示与API的通信成功。

image.png

未来的改进方向

将以下内容用中文进行转述,只需要一个选项:
Workflow的问题在于,我们只是使用Express来连接Cloud Functions和NestJS这两者之间的关系,并且第5个步骤非常复杂。我希望能够通过迁移到Fastify来解决前者的问题,而后者则可以通过使用Schematics进行自动化处理。如果能够自动化第5个步骤,我想应该可以在大约10分钟内完成环境的搭建。

附言

起初我对于是否能在Nx中运行Angular的Schematics感到担心,但实际上发现Nx本身就是Schematics的集合。我之前没有接触过NestJS,但它的感觉非常类似于Angular,看起来很高效。

请喜欢Angular的人务必尝试一下这个环境!

广告
将在 10 秒后关闭
bannerAds