像建筑师一样构建 Angular(第 2 部分)

2025-06-07

像建筑师一样构建 Angular(第 2 部分)

在博客系列“像建筑师一样构建 Angular”的这一部分中,我们将研究如何使用 angular-devkit 优化生产构建,并通过弄清楚如何实现环境来完善我们的自定义构建。

回顾

在《像架构师一样构建 Angular(第一部分)》中,我们学习了如何使用最新的 Architect API。通过使用 Architect API 和 RxJS 编写 Builder,我们能够使用新的生产构建版本来扩展 Angular CLI,该构建版本通过 Closure Compiler 优化了 Angular。

我们最终得到了一个执行 RxJS Observable 的函数,如下所示:

export function executeClosure(
  options: ClosureBuilderSchema,
  context: BuilderContext
): Observable<BuilderOutput> {
  return of(context).pipe(
    concatMap( results => ngc(options, context) ),
    concatMap( results => compileMain(options, context)),
    concatMap( results => closure(options, context) ),
    mapTo({ success: true }),
    catchError(error => {
      context.reportStatus('Error: ' + error);
      return [{ success: false }];
    }),
  );
}
Enter fullscreen mode Exit fullscreen mode

在本节的开始,让我们使用@angular-devkit 中的工具为生产包添加更多优化buildOptimizer

创建一个名为optimizeBuild的新方法,该方法返回一个RxJS Observable,并将该方法添加到pipein中executeClosure

  return of(context).pipe(
    concatMap( results => ngc(options, context) ),
    concatMap( results => compileMain(options, context)),
    concatMap( results => optimizeBuild(options, context)),
    concatMap( results => closure(options, context) ),
Enter fullscreen mode Exit fullscreen mode

@angular-devkit/build-optimizer在 build_tools 目录中安装。

npm i @angular-devkit/build-optimizer --save-dev
Enter fullscreen mode Exit fullscreen mode

buildOptimizer像这样导入。

import { buildOptimizer } from '@angular-devkit/build-optimizer';
Enter fullscreen mode Exit fullscreen mode

本质上,Angular 编译器运行后,out-tsc 中的每个 component.js 文件都需要使用 buildOptimizer 进行后处理。此工具会移除不必要的装饰器,避免文件包变得臃肿。

该脚本的算法如下:

  • 列出 out-tsc 目录中所有扩展名为 .component.js 的文件
  • 读取文件名数组中的每个文件
  • 调用 buildOptimizer,传入每个文件的内容
  • 使用 buildOptimizer 的输出将文件写入磁盘

让我们使用一个名为 glob 的方便的 npm 包来列出具有给定扩展名的所有文件。

在 build_tools 目录中安装 glob。

npm i glob --save-dev
Enter fullscreen mode Exit fullscreen mode

将 glob 导入 src/closure/index.ts。

import { glob } from 'glob';
Enter fullscreen mode Exit fullscreen mode

optimizeBuild方法中,声明一个新的const并调用它files

const files = glob.sync(normalize('out-tsc/**/*.component.js'));
Enter fullscreen mode Exit fullscreen mode

glob.sync将会同步将所有匹配 glob 的文件格式化为一个字符串数组。在上面的例子中,files等于一个字符串数组,其中包含所有扩展名为 的文件的路径.component.js

现在我们有了一个需要使用 进行后处理的文件名数组buildOptimizer。我们的函数optimizeBuild需要返回一个 Observable,但我们有一个文件名数组。

本质上来说optimizeBuild,所有文件处理完毕后才应该发出数据,所以我们需要将文件映射到一个 Observable 数组,并使用一个 RxJS 方法forkJoin等待所有 Observable 处理完毕。构建过程中的下一步是将应用程序与 Closure Compiler 捆绑在一起。该任务必须等待optimizeBuild完成。


const optimizedFiles = files.map((file) => {
    return new Observable((observer) => {
        readFile(file, 'utf-8', (err, data) => {
        if (err) {
            observer.error(err);
        }
        writeFile(file, buildOptimizer({ content: data }).content, (error) => {
            if (error) {
                observer.error(error);
            }
            observer.next(file);
            observer.complete();
        });
    });
    });
});

return forkJoin(optimizedFiles);

Enter fullscreen mode Exit fullscreen mode

每个文件都使用 从磁盘读取readFile,使用 对文件内容进行后处理buildOptimizer,并使用 将结果内容写入磁盘writeFile。观察者调用nextcomplete来通知forkJoin异步操作已执行。

如果在运行此优化之前查看 out-tsc 目录中的文件,这些文件将包含如下装饰器:

AppComponent.decorators = [
{ type: Component, args: [{
            selector: 'app-root',
            templateUrl: './app.component.html',
            styleUrls: ['./app.component.css']
        },] },
];
Enter fullscreen mode Exit fullscreen mode

buildOptimizer现在,装饰器已通过with you run删除architect build_repo:closure_build

让我们继续合并环境,以便我们可以从默认的 Angular CLI 构建中复制此功能。

处理环境

处理环境配置比前面的练习要简单得多。首先我们来看一下问题。

在 src/environments 中默认有两个文件。

  • 环境.ts
  • 环境.产品.ts

environment.prod.ts 默认看起来像这样。

export const environment = {
  production: true
};
Enter fullscreen mode Exit fullscreen mode

src/main.ts 在新搭建的项目中引用此配置。

import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}
Enter fullscreen mode Exit fullscreen mode

请注意,环境对象始终从 ./environments/environment 导入,但每个环境都有不同的文件?

解决方案非常简单。

在 AOT 编译器运行并将 JavaScript 输出到 out-tsc 目录之后但在应用程序捆绑之前,我们必须交换文件。

cp out-tsc/src/environment/environment.prod.js out-tsc/src/environment/environment.js
Enter fullscreen mode Exit fullscreen mode

上面的代码片段使用 cp Unix 命令将生产环境文件复制到默认的 environment.js。

将 environment.js 文件替换为当前环境后,应用程序将被捆绑,并且environment应用程序中所有引用都与正确的环境相对应。

创建一个名为的新函数handleEnvironment,并将选项作为参数传入。该函数与之前的函数类似,返回一个 Observable。

export function handleEnvironment(
    options:ClosureBuilderSchema,
    context: BuilderContext
  ): Observable<{}> {

}
Enter fullscreen mode Exit fullscreen mode

如果我们env在 schema.json 中将其定义为一个选项。

"env": {
    "type": "string",
    "description": "Environment to build for (defaults to prod)."
  }
Enter fullscreen mode Exit fullscreen mode

我们可以使用相同的参数通过 Architect CLI 运行此构建。

architect build_repo:closure_build --env=prod
Enter fullscreen mode Exit fullscreen mode

在我们刚刚创建的方法中,我们可以引用对象env上的参数options

const env = options.env ? options.env : 'prod';
Enter fullscreen mode Exit fullscreen mode

为了复制正确的环境,我们可以使用节点中提供的名为的工具exec

import { exec } from 'child_process';
Enter fullscreen mode Exit fullscreen mode

exec允许您像在终端中一样运行 bash 命令。

execNode.js 中自带的类似函数都是基于 Promise 的。幸运的是,RxJS 的 Observable 可以与 Promise 互操作。我们可以使用ofNode.js 中自带的方法RxJS将其转换exec为 Observable。最终代码如下。

export function handleEnvironment(
    options:ClosureBuilderSchema,
    context: BuilderContext
  ): Observable<{}> {

    const env = options.env ? options.env : 'prod';

    return of(exec('cp '+
                normalize('out-tsc/src/environments/environment.' + env + '.js') + ' ' +
                normalize('out-tsc/src/environments/environment.js')
             ));
}
Enter fullscreen mode Exit fullscreen mode

executeClosure通过再次调用 ,将新方法添加到concatMap。此时,感觉就像针和线一样。

  return of(context).pipe(
    concatMap( results => ngc(options, context) ),
    concatMap( results => compileMain(options, context)),
    concatMap( results => optimizeBuild(options, context)),
    concatMap( results => handleEnvironment(options, context)),
    concatMap( results => closure(options, context) ),
Enter fullscreen mode Exit fullscreen mode

花点时间回顾一下你已成为的构建大师。所有步骤现已到位,可用于正式版构建!

文章来源:https://dev.to/steveblue/build-angular-like-an-architect-part-2-208l
PREV
Web Components 与 React 这不是 Web Components 与 React 的问题
NEXT
面向开发人员的 5 款最佳无代码工具