如何使用 Angular CLI 设置 Nx 风格的 monorepo 工作区:第 1 部分
由 Mux 赞助的 DEV 全球展示挑战赛:展示你的项目!
封面原图由Edgar Chaparro拍摄,来自 Unsplash。
原发布日期:2020年5月10日。
Nrwl 的 Nx 工具链帮助我们在所谓的工作区中工作,工作区是一个单体仓库,可以管理多个应用程序、工作区库和包库。
你可能无法说服你的团队或经理接受 Nx 工具链。如果是这样,你很幸运。在本教程中,我们将逐步讲解如何使用 Angular CLI 而不是 Nx CLI 来设置 Nx 风格的工作区。我们将使用一个自定义的 Node.js 工具来生成应用程序和工作区库项目。
我们将实现 Nrwl Airlines 示例的足够部分,以演示如何在 Angular CLI 工作区中处理多个应用程序,每个应用程序又包含多个平台。这些应用程序域具有一个共享功能。同一域内的两个不同平台通过使用功能外壳库进行编排并作为应用程序项目的入口点,共享完全相同的功能集和路由。
最终我们将得到如下图所示的项目文件夹结构。其中包括 4 个应用程序项目、4 个端到端测试套件和 12 个工作区库。
nrwl-airlines
├── apps
│ ├── booking
│ │ ├── booking-desktop
│ │ ├── booking-desktop-e2e
│ │ ├── booking-mobile
│ │ └── booking-mobile-e2e
│ └── check-in
│ ├── check-in-desktop
│ ├── check-in-desktop-e2e
│ ├── check-in-mobile
│ └── check-in-mobile-e2e
└── libs
├── booking
│ ├── data-access
│ ├── feature-flight-search
│ ├── feature-passenger-info
│ └── feature-shell
├── check-in
│ ├── data-access
│ └── feature-shell
└── shared
├── data-access
├── environments
├── seatmap
│ ├── data-access
│ └── feature-seat-listing
├── ui-buttons
└── util-formatting
Nrwl Airlines 的例子在Nrwl 的免费电子书《企业 Angular Monorepo 模式》中有详细讨论。
首先,我们将使用 Angular schematics 和一些命令行工具,根据我们的规范生成项目,但稍后我们将使用一个名为 的自定义命令行工具来自动化这些操作generate-project。
本教程将分为 5 个部分。在第一部分中,我们将创建 Angular CLI monorepo 工作区,生成预订桌面应用程序项目、其端到端测试项目以及预订功能 shell 工作区库。
Angular 工作区
首先,我们将创建一个名为 `<workspace_name>` 的新 Angular CLI 工作区nrwl-airlines。假设您已全局安装了 Angular CLI。请使用以下ng new命令。
ng new nrwl-airlines --strict --create-application=false
该--strict标志位为 TypeScript 编译器设置了一些严格的配置选项。
清除此--create-application开关可阻止立即生成应用程序项目。这一点很重要,因为我们希望项目采用 Nx 风格的文件和文件夹结构,包括为应用程序和工作区库分别创建单独的文件夹。
nrwl-airlines
├── .editorconfig
├── .gitignore
├── README.md
├── angular.json
├── package.json
├── tsconfig.json
├── tslint.json
└── yarn.lock
我们的空白工作区是根据以下文件和文件夹结构生成的。
npm install --save-dev json
# or
yarn add --dev json
我们将使用该json软件包来编辑工作区中的 JSON 配置。请将其作为开发依赖项安装。
让我们启用 Ivy 引入的严格模板类型检查功能来试试看。运行以下命令。
npx json -I -f tsconfig.json -e "delete this.angularCompilerOptions.fullTemplateTypeCheck"
npx json -I -f tsconfig.json -e "this.angularCompilerOptions.strictTemplates = true"
在我们的 TypeScript 配置中,我们现在有了这些 Angular 编译器选项。
{
"//": "tsconfig.json",
"angularCompilerOptions": {
"strictInjectionParameters": true,
"strictTemplates": true
}
}
我们的大部分项目都将是工作区库。我们将默认项目文件夹设置为libs稍后将创建的文件夹。运行以下ng config命令即可完成此操作。
ng config newProjectRoot libs
预订桌面应用程序
我们将首先生成预订领域的桌面 Web 应用程序。
首先,我们将使用内置的 Angular 应用程序生成器原理图以及这些选项。
ng generate application booking-desktop --prefix=booking --project-root=apps/booking/booking-desktop --style=css --routing=false
接下来,我们将项目文件夹及其配置拆分angular.json为两个项目——一个用于应用程序,一个用于端到端测试。运行以下命令。
npx copy apps/booking/booking-desktop/e2e/**/* apps/booking/booking-desktop-e2e
npx rimraf apps/booking/booking-desktop/e2e
npx json -I -f apps/booking/booking-desktop-e2e/tsconfig.json -e "this.extends = '../../../tsconfig.json'"
npx json -I -f apps/booking/booking-desktop-e2e/tsconfig.json -e "this.compilerOptions.outDir = '../../../out-tsc/e2e'"
ng config projects["booking-desktop"].architect.e2e.options.protractorConfig apps/booking/booking-desktop-e2e/protractor.conf.js
npx json -I -f angular.json -e "this.projects['booking-desktop-e2e'] = this.projects['booking-desktop']"
最后,我们将配置两个预订桌面项目的构建器和架构目标,如这里所示。
ng config projects["booking-desktop-e2e"].root apps/booking/booking-desktop-e2e
ng config projects["booking-desktop-e2e"].architect.lint.options.tsConfig apps/booking/booking-desktop-e2e/tsconfig.json
npx json -I -f angular.json -e "delete this.projects['booking-desktop'].architect.e2e"
npx json -I -f angular.json -e "this.projects['booking-desktop'].architect.lint.options.tsConfig.pop()"
ng config projects["booking-desktop"].architect.lint.options.exclude[1] !apps/booking/booking-desktop/**
npx json -I -f angular.json -e "delete this.projects['booking-desktop-e2e'].architect.build"
npx json -I -f angular.json -e "delete this.projects['booking-desktop-e2e'].architect['extract-i18n']"
npx json -I -f angular.json -e "delete this.projects['booking-desktop-e2e'].architect.serve"
npx json -I -f angular.json -e "delete this.projects['booking-desktop-e2e'].architect.test"
npx json -I -f angular.json -e "delete this.projects['booking-desktop-e2e'].prefix"
npx json -I -f angular.json -e "delete this.projects['booking-desktop-e2e'].sourceRoot"
npx json -I -f angular.json -e "delete this.projects['booking-desktop-e2e'].schematics"
ng config projects["booking-desktop-e2e"].architect.lint.options.exclude[1] !apps/booking/booking-desktop-e2e/**
有些命令只是在拆分项目文件夹和工作区配置条目后进行的清理工作。有些命令则模拟了我们使用 Nx CLI 和 Nrwl schematics for Angular 时获得的配置。
apps
└── booking
├── booking-desktop
│ ├── src
│ │ ├── app
│ │ │ ├── app.component.css
│ │ │ ├── app.component.html
│ │ │ ├── app.component.spec.ts
│ │ │ ├── app.component.ts
│ │ │ └── app.module.ts
│ │ ├── assets
│ │ │ └── .gitkeep
│ │ ├── environments
│ │ │ ├── environment.prod.ts
│ │ │ └── environment.ts
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── main.ts
│ │ ├── polyfills.ts
│ │ ├── styles.css
│ │ └── test.ts
│ ├── browserslist
│ ├── karma.conf.js
│ ├── tsconfig.app.json
│ ├── tsconfig.spec.json
│ └── tslint.json
└── booking-desktop-e2e
├── src
│ ├── app.e2e-spec.ts
│ └── app.po.ts
├── protractor.conf.js
└── tsconfig.json
经过这些步骤后,我们的apps目录文件结构如上图所示。
{
"//": "angular.json",
"projects": {
"booking-desktop": {
"projectType": "application",
"schematics": {},
"root": "apps/booking/booking-desktop",
"sourceRoot": "apps/booking/booking-desktop/src",
"prefix": "booking",
"architect": {
"build": {
"//": "(...)"
},
"serve": {
"//": "(...)"
},
"extract-i18n": {
"//": "(...)"
},
"test": {
"//": "(...)"
},
"lint": {
"//": "(...)"
}
}
}
}
}
如上所示,booking-desktop应用程序配置angular.json看起来像是默认配置,只是我们删除了e2e架构师目标。
{
"//": "angular.json",
"projects": {
"booking-desktop-e2e": {
"projectType": "application",
"root": "apps/booking/booking-desktop-e2e",
"architect": {
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": "apps/booking/booking-desktop-e2e/tsconfig.json",
"exclude": ["**/node_modules/**", "!apps/booking/booking-desktop-e2e/**"]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "apps/booking/booking-desktop-e2e/protractor.conf.js",
"devServerTarget": "booking-desktop:serve"
},
"configurations": {
"production": {
"devServerTarget": "booking-desktop:serve:production"
}
}
}
}
}
}
}
上面的列表显示了booking-desktop-e2e项目配置,其中包含e2e架构lint目标。
运行以下命令进行测试。
ng run booking-desktop-e2e:lint
ng run booking-desktop-e2e:e2e
集中 Karma 配置
由于我们将单体仓库拆分成多个项目,每个项目都有独立的测试构建器,因此它们需要各自的 Karma 配置。由于它们大部分内容相同,我们将在工作区根文件夹中创建一个通用的 Karma 配置文件,如本清单所示。
// karma.conf.js
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
const { constants } = require('karma');
const path = require('path');
module.exports = () => ({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [require('karma-jasmine'), require('karma-chrome-launcher'), require('karma-jasmine-html-reporter'), require('karma-coverage-istanbul-reporter'), require('@angular-devkit/build-angular/plugins/karma')],
client: {
clearContext: false, // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: path.join(__dirname, 'coverage'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true,
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: constants.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true,
});
我们将把桌面应用程序项目的 Karma 配置替换为以下列表中所示的配置。
// apps/booking/booking-desktop/karma.conf.js
const path = require('path');
const getBaseKarmaConfig = require('../../../karma.conf');
module.exports = (config) => {
const baseConfig = getBaseKarmaConfig();
config.set({
...baseConfig,
coverageIstanbulReporter: {
...baseConfig.coverageIstanbulReporter,
dir: path.join(__dirname, '../../../coverage/apps/booking/booking-desktop'),
},
});
};
运行以下命令,确保应用程序的单元测试仍然有效。
ng run booking-desktop:test --watch=false
预订功能外壳库
第一个应用程序项目设置完毕,我们就可以创建预订应用程序的功能外壳库了。
npm install --save-dev rimraf
# or
yarn add --dev rimraf
我们将使用命令行工具rimraf删除一些在使用 Angular 库 schematic 时创建的文件。原因是 Angular 库 schematic 会生成一个包库,用于发布到 NPM 等包注册表中。
在使用工作区时,我们经常会创建工作区库,这些库可能专用于单个应用程序,也可能在多个应用程序之间共享。然而,通常无需对其进行打包、版本控制和发布。这正是使用单体仓库的优势之一。
ng config newProjectRoot libs/booking
如上文所述,我们首先设置要生成的库项目的父文件夹。
现在我们使用 Angular 库 schematic 通过以下命令生成库项目。
ng generate library feature-shell --prefix=booking --entry-file=index --skip-install --skip-package-json
使用 Angular 库生成器示意图,我们可以得到以下文件和文件夹结构。
libs/booking/feature-shell
├── src
│ ├── lib
│ │ ├── feature-shell.component.spec.ts
│ │ ├── feature-shell.component.ts
│ │ ├── feature-shell.module.ts
│ │ ├── feature-shell.service.spec.ts
│ │ └── feature-shell.service.ts
│ ├── index.ts
│ └── test.ts
├── README.md
├── karma.conf.js
├── ng-package.json
├── package.json
├── tsconfig.lib.json
├── tsconfig.lib.prod.json
├── tsconfig.spec.json
└── tslint.json
我们将删除这些文件ng-package.json,package.json因为tsconfig.lib.prod.json我们希望这是一个工作区库,而不是一个包库。
我们还将移除该服务及其测试套件,并将 Angular 模块重命名,以遵循 Nx 命名约定。
npx json -I -f angular.json -e "this.projects['booking-feature-shell'] = this.projects['feature-shell']"
npx json -I -f angular.json -e "delete this.projects['feature-shell']"
使用上述命令重命名工作区库项目。我们将参数传递feature-shell给name库原理图,因为我们希望将文件夹命名为“项目名称”,而该文件夹已嵌套在该libs/booking文件夹中。
这样,库项目配置中使用的文件路径就正确了。
npx json -I -f angular.json -e "delete this.projects['booking-feature-shell'].architect.build"
运行上面的命令来移除build架构师目标,因为我们不会单独构建这个库——相反,它将作为预订应用程序的一部分进行构建。
我们将留下test并lint构建目标。
ng config projects["booking-feature-shell"].architect.lint.options.exclude[1] !libs/booking/feature-shell/**
npx json -I -f libs/booking/feature-shell/tslint.json -e "this.linterOptions = { exclude: ['!**/*'] }"
我们将应用在使用 Nrwl 的 Angular schematics 时设置的 linter 配置。运行以上命令。
如前所述,我们将使用以下命令删除不必要的文件。
npx rimraf libs/booking/feature-shell/*package.json
npx rimraf libs/booking/feature-shell/tsconfig.lib.prod.json
npx rimraf libs/booking/feature-shell/src/lib/*.*
我们生成具有约定俗成名称的功能外壳 Angular 模块,并按如下列表所示导出它。
ng generate module booking-feature-shell --project=booking-feature-shell --flat --no-common-module
"export * from './lib/booking-feature-shell.module';" > libs/booking/feature-shell/src/index.ts
让我们也为功能外壳 Angular 模块添加一个测试套件,内容如下。
// booking-feature-shell.module.spec.ts
import { TestBed } from '@angular/core/testing';
import { BookingFeatureShellModule } from './booking-feature-shell.module';
describe('BookingFeatureShellModule', () => {
beforeEach(async () => {
TestBed.configureTestingModule({
imports: [BookingFeatureShellModule],
});
await TestBed.compileComponents();
});
it('should create', () => {
expect(BookingFeatureShellModule).toBeDefined();
});
});
我们还会添加一个外壳组件。
ng generate component shell --project=booking-feature-shell --module=booking-feature-shell.module.ts --display-block
"<router-outlet></router-outlet>" > libs/booking/feature-shell/src/lib/shell/shell.component.html
我们将路由器模块添加到其组件测试套件中。
// shell.component.spec.ts
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterModule } from '@angular/router';
import { ShellComponent } from './shell.component';
describe('ShellComponent', () => {
let component: ShellComponent;
let fixture: ComponentFixture<ShellComponent>;
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [ShellComponent],
imports: [RouterModule.forRoot([])],
});
TestBed.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ShellComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
现在我们来修改预订功能外壳 Angular 模块的内容。
// booking-feature-shell.module.ts
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ShellComponent } from './shell/shell.component';
const routes: Routes = [
{
path: '',
component: ShellComponent,
children: [],
},
];
@NgModule({
declarations: [ShellComponent],
exports: [RouterModule],
imports: [RouterModule.forRoot(routes), CommonModule],
})
export class BookingFeatureShellModule {}
shell 组件是我们布局和功能的入口组件。每个额外的路由都会添加到childrenshell 组件的路由数组中。
其他路由将由 shell 组件模板中的路由出口渲染。
Angular 库示意图在以下列表中设置了路径映射。
{
"//": "tsconfig.json",
"compilerOptions": {
"paths": {
"feature-shell": ["dist/feature-shell/feature-shell", "dist/feature-shell"]
}
}
}
该路径映射的目的是在软件包库构建完成并输出到dist文件夹后使用它。
我们不再build为该库设置架构目标。请记住,单体仓库直接从源代码中使用工作区库,并将其构建为应用程序包的一部分。
npx json -I -f tsconfig.json -e "delete this.compilerOptions.paths['feature-shell']"
npx json -I -f tsconfig.json -e "this.compilerOptions.paths['@nrwl-airlines/booking/feature-shell'] = ['libs/booking/feature-shell/src/index.ts']"
上面的列表展示了如何设置一个路径映射别名,该别名使用我们选择的 NPM 作用域,即 `<library_name>` @nrwl-airlines。该映射指向库的公共 API 桶文件,index.ts如下面的列表所示。
{
"//": "tsconfig.json",
"compilerOptions": {
"paths": {
"@nrwl-airlines/booking/feature-shell": ["libs/booking/feature-shell/src/index.ts"]
}
}
}
现在应用程序可以通过使用@nrwl-airlines/booking/feature-shell路径导入功能 shell 库。
请记住将库的 Karma 配置替换为以下配置。
// libs/booking/feature-shell/karma.conf.js
const path = require('path');
const getBaseKarmaConfig = require('../../../karma.conf');
module.exports = (config) => {
const baseConfig = getBaseKarmaConfig();
config.set({
...baseConfig,
coverageIstanbulReporter: {
...baseConfig.coverageIstanbulReporter,
dir: path.join(__dirname, '../../../coverage/libs/booking/feature-shell'),
},
});
};
请使用以下命令运行代码检查器和单元测试套件,以确保我们已正确设置所有内容。
ng run booking-feature-shell:lint
ng run booking-feature-shell:test --watch=false
以下列表显示了预订功能外壳库生成的文件和文件夹结构。
libs/booking/feature-shell
├── src
│ ├── lib
│ │ ├── booking-feature-shell.module.spec.ts
│ │ └── booking-feature-shell.module.ts
│ ├── index.ts
│ └── test.ts
├── README.md
├── karma.conf.js
├── tsconfig.lib.json
├── tsconfig.spec.json
└── tslint.json
预订功能 shell 库有两个架构目标test,lint如下表所示。
{
"//": "angular.json",
"projects": {
"booking-feature-shell": {
"projectType": "library",
"root": "libs/booking/feature-shell",
"sourceRoot": "libs/booking/feature-shell/src",
"prefix": "booking",
"architect": {
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "libs/booking/feature-shell/src/test.ts",
"tsConfig": "libs/booking/feature-shell/tsconfig.spec.json",
"karmaConfig": "libs/booking/feature-shell/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": ["libs/booking/feature-shell/tsconfig.lib.json", "libs/booking/feature-shell/tsconfig.spec.json"],
"exclude": ["**/node_modules/**", "!libs/booking/feature-shell/**"]
}
}
}
}
}
}
现在我们已经生成并配置了功能外壳库项目,让我们将功能外壳模块导入到预订桌面应用程序中。
在初始预订功能框架中,我们已在顶层路由中使用框架组件准备好了根路由配置。稍后,我们将添加功能路由、配置和初始化。
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BookingFeatureShellModule } from '@nrwl-airlines/booking/feature-shell';
import { AppComponent } from './app.component';
@NgModule({
bootstrap: [AppComponent],
declarations: [AppComponent],
imports: [BrowserModule, BookingFeatureShellModule],
})
export class AppModule {}
前面的列表显示了我们如何预先加载预订功能外壳模块,这没问题,因为它反过来会延迟加载功能路由。
<!-- apps/booking/booking-desktop/src/app/app.component.html -->
<h1>{{title}}</h1>
<router-outlet></router-outlet>
请记住,要像上面的清单中那样,在预订桌面应用程序的根组件中添加一个路由出口。
在应用程序组件测试套件中进行相应的更改。
// apps/booking/booking-desktop/src/app/app.component.spec.ts
import { async, TestBed } from '@angular/core/testing';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [AppComponent],
imports: [RouterModule.forRoot([])],
}).compileComponents();
}));
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'booking-desktop'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('booking-desktop');
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('booking-desktop');
});
});
同时更新端到端测试。我们将标题与内容进行匹配'booking-mobile',并将选择器更新为booking-root h1。
// apps/booking/booking-desktop-e2e/src/app.e2e-spec.ts
import { browser, logging } from 'protractor';
import { AppPage } from './app.po';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('booking-desktop');
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(
jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry)
);
});
});
// apps/booking/booking-desktop-e2e/src/app.po.ts
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo(): Promise<unknown> {
return browser.get(browser.baseUrl) as Promise<unknown>;
}
getTitleText(): Promise<string> {
return element(by.css('booking-root h1')).getText() as Promise<string>;
}
}
对我们的应用程序和端到端项目进行代码检查和测试,以确保一切正常运行。
ng run booking-desktop:lint
ng run booking-desktop:test --watch=false
ng run booking-desktop-e2e:lint
ng run booking-desktop-e2e:e2e
ng run booking-feature-shell:lint
ng run booking-feature-shell:test --watch=false
现在运行程序ng run booking-desktop:serve,并http://localhost:4200在浏览器中访问我们的应用程序。它应该如下图所示。
结论
目前,我们的项目文件夹结构如下图所示。
nrwl-airlines
├── apps
│ └── booking
│ ├── booking-desktop
│ └── booking-desktop-e2e
└── libs
└── booking
└── feature-shell
我们首先使用 Angular CLI 创建了一个没有任何应用程序或库项目的空白工作区。
为了学习如何使用json命令行工具修改基于 JSON 的配置文件,我们启用了严格的模板类型检查。
我们创建的第一个项目是预订桌面应用程序。在使用官方 Angular 应用程序项目结构体生成该项目后,我们将端到端测试套件及其配置提取到一个单独的项目中,lint并e2e设置了架构目标。
我们通过在工作区根目录下设置基础配置来集中管理 Karma 配置。预订桌面应用程序项目通过为其测试覆盖率报告设置唯一路径来扩展此基础配置。
接下来,我们创建了第一个工作区库——预订功能框架库。我们使用了官方的 Angular 库结构图来构建起点。然而,该结构图旨在为发布到 NPM 等包注册表上的包库搭建框架。
工作区库仅在单体仓库内部使用。它们可以在多个项目之间共享,并且由于其源代码与工作区中的其他项目一起存在,因此不需要版本控制。
我们设置了一个入口点 Angular 模块,并为入口点 Angular 组件(外壳组件)设置了路由。该外壳组件本身将有子路由,用于应用程序的其余部分,这些子路由由其路由出口动态渲染。
为了让应用程序能够导入预订功能外壳库,我们设置了 TypeScript 路径映射。然后,我们向应用程序组件添加了一个路由出口,并导入了预订功能外壳模块,从而将这两个部分连接起来。
如您所知,随着代码库的演进,维护它至关重要。我们更新了单元测试和端到端测试套件,以使其与变更保持同步。
最后,我们对整个代码库进行了代码检查,并运行了单元测试和端到端测试套件。
今天的工作量就到此为止了。
在第二部分中,我们将使用自定义项目生成工具来自动化今天完成的许多手动工作。我们将使用该工具生成共享和预订数据访问库,然后将 NgRx Store 和 Effects 的根状态和功能状态添加到其中。最后,我们将添加 NgRx Store DevTools 和 NgRx schematics,在功能外壳库中注册数据访问库,并提取共享环境工作区库,以便能够按照我们想要的方式配置数据访问。
资源
对于性急的程序员来说,完整的解决方案在LayZeeDK/ngx-nrwl-airlines-workspaceGitHub 代码库中。
