如果你正在 NX workspace 中使用Angular,并且迫不及待地想尝试最新的 Angular 功能,那么这篇文章适合你。
New features新功能
您可能已经熟悉 Angular 17 以及它带来了什么。除了新徽标和令人印象深刻的新文档页面(angular.dev)之外,它还提供了一些很棒的新功能:
- New control flow syntax (@if, @else, etc.) 新的控制流语法(@if、@else等)
- Faster builds with ESBuild 使用 ESBuild 加快构建速度
- Deferrable views可延迟视图
- Enhanced SSR support增强的 SSR 支持
新的控制流和更快的构建从根本上改善了开发人员体验,为尽快更新项目提供了强大的动力。
升级步骤
本文介绍了一个示例更新过程,包括特定于项目的详细信息和潜在挑战。但是,在您自己的项目中,使用您独特的设置,您可能会遇到不同的障碍。
在 NX 工作区中,更新过程应与执行以下命令一样简单:
// Updates your dependencies in package.json
// and generates a migrations.json file
npx nx migrate latest
// Installs the new dependencies
npm install
// Executes the migration scripts based on migrations.json one-by-one
npx nx migrate --run-migrations</pre>
从理论上讲,这听起来很简单。在实践中,经常会出现问题。
Migrate latest命令的作用
在继续之前,让我们先看看有什么 npx nx migrate latest
作用。
此命令将 package.json 文件中的所有 @nx/* 和 @angular/* 依赖项更新到最新版本。我发现的值得注意的例外是 @angular/cli,它在迁移过程中稍后进行了更新。
此外,它还会生成一个 migrations.json 文件,其中包含应在代码上执行的所有脚本以采用更改 - 例如。自动配置更改等。
Npm install 的问题
剧透: npm install --force
解决了循环依赖项问题,该问题在以后的全新安装期间不会导致任何问题 ( npm ci
)。
细节:在一些场景下, npm install
失败并出现此错误:
npm ERR! While resolving: ev-portals@0.0.0
npm ERR! Found: @angular-devkit/build-angular@16.2.1
npm ERR! node_modules/@angular-devkit/build-angular
npm ERR! dev @angular-devkit/build-angular@"17.0.0" from the root project
npm ERR! peer @angular-devkit/build-angular@">= 14.0.0 < 17.0.0" from @nx/angular@16.8.1
npm ERR! node_modules/@nx/angular
npm ERR! @nx/angular@"17.1.1" from the root project
npm ERR! @nx/angular@"16.8.1" from @nrwl/angular@16.8.1
npm ERR! node_modules/@nrwl/angular
npm ERR! @nrwl/angular@"16.8.1" from @nx/angular@16.8.1
npm ERR! 2 more (@storybook/angular, jest-preset-angular)</pre>
这意味着,我的 @nx/angular@16.8.1 包仍然希望使用比现在在 package.json 文件 (17.0.0) 中定义的版本更早的 @angular-devkit/build-angular。奇怪的是,我的package.json在更新后已经包含@nx/angular@17.1.1......因此,首先我尝试只安装这个包,因此它优先。
npm i @nx/angular@17.1.1
它再次失败了,报错:
npm ERR! peer @angular-devkit/build-angular@">=14.1.0 < 17.0.0" from @storybook/angular@7.0.18
虽然我在package.json文件中已经有@storybook/angular@7.5.3...所以我继续这个单独的安装。
npm i @storybook/angular@7.5.3
这次的结果是
npm ERR! While resolving: ev-portals@0.0.0
npm ERR! Found: @angular-devkit/build-angular@16.2.1
npm ERR! node_modules/@angular-devkit/build-angular
npm ERR! peer @angular-devkit/build-angular@">= 14.0.0 < 17.0.0" from @nx/angular@16.8.1
npm ERR! node_modules/@nx/angular
npm ERR! @nx/angular@"16.8.1" from @nrwl/angular@16.8.1
npm ERR! node_modules/@nrwl/angular
npm ERR! @nrwl/angular@"16.8.1" from @nx/angular@16.8.1
npm ERR! @nx/angular@"17.1.1" from the root project
npm ERR! peer @angular-devkit/build-angular@">=14.1.0 < 17.0.0" from @storybook/angular@7.0.18
npm ERR! node_modules/@storybook/angular
npm ERR! dev @storybook/angular@"7.5.3" from the root project
npm ERR! 2 more (jest-preset-angular, the root project)</pre>
似乎我们回到了根目录,@nx/angular 包又来了,我们只是尝试单独安装。.在这一点上,由于我找不到单个软件包安装的正确组合或顺序,我选择了 --force install,然后,我检查了全新安装,一切按预期工作。
运行升级命令
在执行时值得注意控制台输出,因为它提供了正在执行 npx nx migrate --run-migrations
的内容的分步详细信息。
Ran 17.0.0-move-cache-directory from nx
Updates the default cache directory to .nx/cache
UPDATE .gitignore
UPDATE .prettierignore
Ran 17.0.0-use-minimal-config-for-tasks-runner-options from nx
Use minimal config for tasksRunnerOptions
UPDATE package.json
UPDATE nx.json
[object Object]
Ran rm-default-collection-npm-scope from nx
Migration for v17.0.0-rc.1
UPDATE nx.json
Ran move-options-to-target-defaults from @nx/jest
Move jest executor options to nx.json targetDefaults
UPDATE nx.json
UPDATE apps/sample-app/project.json
// Same repeats for all projects
Ran update-angular-cli-version-17-0-0 from @nx/angular
Update the @angular/cli package version to ~17.0.0.
UPDATE package.json
Ran rename-browser-target-to-build-target from @nx/angular
Rename 'browserTarget' to 'buildTarget'.
UPDATE apps/sample-app/project.json
// Same repeats for all apps
Ran update-17-0-0-rename-to-eslint from @nx/linter
update-17-0-0-rename-to-eslint
UPDATE package.json
UPDATE apps/sample-app/project.json
// Same repeats for all projects
UPDATE migrations.json
Ran block-template-entities from @angular/core
Angular v17 introduces a new control flow syntax that uses the @ and } characters. This migration replaces the existing usages with their corresponding HTML entities.
UPDATE libs/../../x.component.html
// Plus all other templates, where we had the '@' sign gets encoded to '@'
Skipping migration for project sample-project. Unable to determine 'tsconfig.json' file in workspace config.
// Same repeats for all projects (apps and libs)
? Updated Angular Material to version 17
Skipping migration for project sample-project. Unable to determine 'tsconfig.json' file in workspace config.
// Same repeats for all projects (apps and libs)
总而言之,在根文件夹中引入了一个新的 .nx 缓存文件夹,更新了 @angular/cli,@ 和 { 字符在模板中被替换为它们的 HTML 实体——由于新的控制流语法——并且执行了一堆 nx.json 和 project.json 配置更改,例如将 @nx/linter:eslint 重命名为 @nx/eslint:lint。
好的,太好了,我们成功地更新了项目,或者,不是吗?当我尝试验证项目(通过linting,构建,测试)时,它抛出了以下错误:
Unable to resolve @nrwl/linter:eslint
这是某些project.json文件中的lint执行程序,似乎在某些项目中它没有被替换到较新版本,因此我不得不手动更新这些project.json文件:
"lint": {
"executor": "@nx/eslint:lint",
...
在运行linting期间,可能会发生一些以前没有的错误,即:
@typescript-eslint/no-unused-vars
error 'x' is defined but never used @typescript-eslint/no-unused-vars
and @typescript-eslint/no-explicit-any 和 @typescript-eslint/no-explicit-any
error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
不完善的快速解决方法是将错误切换为警告 - 就像它们最初一样 - 只需更改您的全局 eslint 配置:
{
"files": [".ts", ".tsx"],
"extends": ["plugin:@nx/typescript"],
"rules": {
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-unused-vars": "warn",
...
所以现在我们可以说,我们迁移到了最新版本的 @nx 并成功@angular。尽管我们仍然有旧的 ngFor、ngIf 等控制流程,因为到目前为止,nx 没有提供自动更新迁移脚本。
采用Angular17 新的控制流语法
幸运的是,@angular/core 在这里提供了支持。只需在 NX 工作区的根文件夹中运行 ng g @angular/core:control-flow
即可。请注意,此脚本处于开发者预览阶段,因此请谨慎使用。
ng g @angular/core:control-flow
在我们应用自动更新并执行测试后,我们收到了一条新的错误信息:
@switch block can only contain @case and @default blocks
事实证明,我们在 中使用了 if
中的语句,但没有在 case
中使用 switch
.这相对容易解决,只需将 if
语句移出 switch
.
后来,我们注意到,模板的格式不像以前那么漂亮,所有更新的文件中都缺少正确的缩进。
如果您使用的是 prettier,最简单的解决方案是在模板上运行它:
npx prettier ./*/.html --write
当然,您可以根据需要随意更改通配符 ./**/*.html
路径。在你这样做之前,你可能还想更新到 prettier@3.1.0,因为它增加了对 Angular 新控制流语法的额外支持
npm i prettier@3.1.0 --save-dev
注意:我还必须重新启动我的 VSCode,否则新版本的 prettier 无法在编辑器中开始工作。
运行prettier仍然会遇到所谓的 ICU 表达式(由 angular i18n 支持)上的错误
SyntaxError: Unexpected closing block. The block may have been closed earlier. If you meant to write the } character
在这里,您可以决定等待更漂亮的修复程序,或者通过计算代码中的正确字符串来替换它们。
采用新的基于 ESBuild 的构建器
好的,到目前为止很棒,但不要止步于此。Angular 17 还为我们带来了一个伟大的新构建器,它比以往任何时候都更快——这要归功于 esbuild。
为了更好地了解发生了什么变化以及具体变化如何,值得一看 Angular 文档页面。
简而言之,您有两种选择,要么使用
- **browser builder ** — 更易于采用
- **application builder **— 更健壮
如果您使用浏览器构建器,您唯一需要更改的是相应 project.json 文件中的构建器名称
@angular-devkit/build-angular:browser
改为
@angular-devkit/build-angular:browser-esbuild
所以我们添加了一个 -esbuild
后缀,8 个字母,我们有一个更快的构建,很划算,对吧?
不过,推荐的方法是使用应用程序构建器,它可以让您更好地为未来做好准备,例如通过提供 SSR 或 SSG 功能。
在这种情况下,您必须在 @angular-devkit/build-angular:application
angular.json中使用,您必须将选项重命名为 browser
,并删除一堆不再相关的 main
选项:buildOptimizer,resourcesOutputPath,vendorChunk,commonChunk,deployUrl,ngswConfigPath
就是这样,也不是那么疯狂。
还要注意,随着新版本的推出,您所拥有的一切都 dist/apps/x
将移动到 dist/apps/x/browser.
回顾
也许从以前版本的 nx 和 angular 升级到 v17 比简单地运行 1-2 个命令要多一些努力,但这绝对是值得的。
Angular Renaissance 正处于最佳状态,新功能极大地改善了 DX,所以不要犹豫,试试吧!