Angular中文博客

分享让你更聪明

[译2024]认识 Angular v19,Angular v19已发布

浏览:92次 评论:0次 日期:2024年11月21日 22:26:14 作者:管理员

在过去的两年里,我们加倍加大了对开发人员体验和性能的投资——在每一个版本中,我们都在不断地进行改进,这些改进结合在一起会产生更大的影响。

看到社区的积极响应和开发者活动参与度的提高,证明我们一直在朝着正确的方向前进。

今天的版本带来了一系列改进,让您能够更加轻松、自信地交付快速的 Web 应用程序。

Angular v19的几个亮点:

您可以从我们的特别活动视频中观看此次发布的快速概述。有关 v19 的功能和改进的更全面列表,请继续阅读下文。

视频链接:https://youtu.be/JvkX2_46gUY

为速度而打造(Build for speed)

不断发展的 Angular 我们看到了实现开箱即用的最佳性能实践的机会,以便我们可以支持您对性能敏感的用例。在过去的两年里,我们启动了一个支持无区域 Angular 的项目,使服务器端渲染成为 Angular CLI 的一个组成部分,并与Chrome Aurora在水合作用和图像指令方面密切合作。

在版本 19 中,我们通过增量水合、服务器路由配置、默认启用的事件重放等将 Angular 服务器端渲染提升到另一个水平。

构建大型 Web 应用程序会增加我们发送给用户的 JavaScript 数量,这会对用户体验产生负面影响。在 v17 中,我们使延迟加载具有客户端渲染应用程序的可延迟视图的代码变得微不足道。对于服务器端渲染的应用程序,我们引入了完整的应用程序水合作用,它需要与给定页面关联的所有 JavaScript 来使其具有交互性。今天,我们正在向服务器提供一个受可延迟视图启发的解决方案!

欢迎在开发者预览版中增加水合(hydration)作用

增量水合允许您使用已经熟悉的@defer语法来注释模板的某些部分,指示 Angular 在特定触发器上延迟加载和水合它们。

增量补水演示

上面的演示显示了服务器端渲染页面的增量水合作用。我们在演示应用程序代码中添加了三种视觉效果,以更好地说明正在发生的情况:

此外,演示应用程序为每个加载操作人为延迟了 500 毫秒,以便我们可以轻松探索不同的状态。

请注意,一开始除了顶部栏之外的所有内容都是灰色的。这意味着此时我们尚未下载与该页面关联的任何 JavaScript。当用户使用左上角的过滤器组件时,Angular 会下载它(通过脉冲直观地指示),然后将其水化(通过组件周围的紫色光芒来证明)。

随后,我们继续与页面交互并逐步水合其余组件。

即使没有人为延迟,Angular 也会异步下载和水合组件,这意味着我们必须重播用户事件。对于此功能,我们使用我们在 Angular 版本 18 中引入的事件重播功能,该功能为 Google Search 提供支持。

当您更新到 Angular v19 时,您可以在任何已使用 SSR 和完整应用程序水化的应用程序中尝试新的增量水化。在您的客户端引导程序中,指定:

import { provideClientHydration, withIncrementalHydration } from '@angular/platform-browser';
// ...
provideClientHydration(withIncrementalHydration());

要将增量水合应用到模板的一部分,请使用:

@defer (hydrate on viewport) {
  <shopping-cart/>
}

当您的应用程序加载时,Angular 不会下载并填充购物车组件,直到它进入视口。您可以在文档中阅读有关增量补水的更多信息。

我们感谢所有在增量水合 RFC中分享想法的人以及我们的 Beta 测试人员。感谢您帮助我们改进 Angular!

默认情况下启用事件重播

任何框架中服务器端渲染应用程序的一个常见问题是用户事件与浏览器下载和执行负责处理该事件的代码之间出现的间隙。我们已经在增量补水部分谈到了这一点。

去年五月,我们分享了事件调度库,它解决了这个用例。事件分派在初始页面加载期间捕获事件,并在负责处理事件的代码可用时重放这些事件。事件调度与 Wiz 团队为 Google 搜索开发的相同,在过去十年中已经过数十亿用户的实际测试。

您可以通过配置您的水合提供程序来启用 Angular 中的事件重播功能:

// For new projects the Angular CLI will generate this code by default
bootstrapApplication(App, {
  providers: [
    provideClientHydration(withEventReplay())
  ]
});

结果将类似于下面 gif 中的可视化效果。当浏览器第一次渲染应用程序时,它还没有下载任何 JavaScript,我们通过使用灰色的 UI 来可视化。您可以看到,与此同时,用户多次单击“添加到购物车”按钮。在后台,事件调度记录所有这些事件。当负责处理点击事件的 JavaScript 加载时,事件分派会重播反映在购物车中的商品数量的事件:

在过去的六个月里,我们验证了这种方法对于 Angular 来说确实非常有效。今天,我们将事件重播升级到稳定状态,并默认为所有使用服务器端渲染的新应用程序启用它!

路由级渲染模式

当您在应用程序中启用服务器端渲染时,默认情况下,Angular 将在服务器端渲染应用程序中的所有参数化路由,并预渲染所有不带参数的路由。

在 v19 中,我们提供了一个名为ServerRoute的新接口,它允许您配置各个路由是否应在服务器端渲染、预渲染或在客户端渲染:

export const serverRouteConfig: ServerRoute[] = [
  { path: '/login', mode: RenderMode.Server },
  { path: '/dashboard', mode: RenderMode.Client },
  { path: '/**', mode: RenderMode.Prerender },
];

在上面的示例中,我们指定希望 Angular 在服务器上渲染login路由,在客户端上渲染dashboard路由,并预渲染所有其他路由。服务器路由配置是一个新的配置文件,但它用 glob 组成了现有的路由声明,因此您不必复制任何路由。

过去,没有符合人体工程学的方法来在预渲染时解析路线参数。通过服务器路由配置,现在这是无缝的:

export const routeConfig: ServerRoute = [{
 path: '/product/:id',
 mode: 'prerender',
 async getPrerenderPaths() {
   const dataService = inject(ProductService);
   const ids = await dataService.getIds(); // ["1", "2", "3"]
   return ids.map(id => ({ id })); // `id` is used in place of `:id` in the route path.
  },
}];

由于 Angular 在注入上下文中执行getPrerenderPaths ,因此您可以使用inject在参数解析中重用业务逻辑。

此功能现已提供开发者预览版!您可以在我们的文档中阅读有关路由级渲染模式的更多信息。

使用 Zoneless Angular 进行服务器端渲染

在 v18 中,我们引入了对 zoneless 的实验性支持,它允许 Angular 在不依赖 zone.js 的情况下运行。从历史上看,zone.js 一直是 Angular 服务器端渲染故事中的关键组件,当框架完成渲染并且页面标记准备就绪时,它会通知服务堆栈。

我们发现等待应用程序的主要原因是待处理的请求和导航。我们引入了一个在 Angular HttpClientRouter中使用的原语,用于延迟向用户发送页面,直到应用程序准备就绪。今天您就可以在 v19 中尝试这两个软件包和 zoneless!

除此之外,我们还提供了一个 RxJS 运算符,它使您能够通知服务堆栈 Angular 尚未完成渲染:

subscription
  .asObservable()
  .pipe(
    pendingUntilEvent(injector),
    catchError(() => EMPTY),
  )
  .subscribe();

subscription发出新值时,我们将使应用程序稳定,并且服务堆栈会将渲染的标记传递给客户端。

开发者体验

我们一直致力于让您从一开始就能够构建快速的应用程序。我们认为确保您有效地开发这些应用程序同样重要。今天,我们迫不及待地想与您分享一些令人兴奋的改进!

通过热模块更换进行即时编辑/刷新

Angular v19 支持开箱即用的样式的热模块替换 (HMR),并为标志后面的模板 HMR 提供实验性支持!

在此改进之前,每次您更改组件的样式或模板并保存文件时,Angular CLI 都会重建您的应用程序并向浏览器发送刷新通知。

我们的新 HMR 将编译您修改的样式或模板,将结果发送到浏览器,并修补您的应用程序,而无需刷新页面和任何状态丢失。这样您将拥有更快的周转周期和不间断的流程状态。

Angular CLI 中的热模块替换样式

v19 中默认启用样式的热模块替换!要尝试模板的 HMR,请使用:

NG_HMR_TEMPLATES=1 ng serve

要禁用此功能,请指定"hmr": false作为开发服务器选项,或者使用:

ng serve --no-hmr

standalone默认为 true

两年多前,我们在 v14 中引入了独立组件。在上次开发者调查中,超过 90% 的开发者表示他们正在使用此功能。作为 v19 的一部分,我们提供了一个原理图,它将作为ng update的一部分运行,并将自动删除所有独立指令、组件和管道standalone组件元数据属性,并将所有非独立抽象的standalone设置为false 。

有关更多信息,请查看update.angular.dev上的更新指南。感谢Matthieu Riegler 的贡献!

严格standalone模式

为了帮助您在项目中实施现代 API,我们开发了一个编译器标志,如果它发现不独立的组件、指令或管道,则会抛出错误。要在项目中启用它,请配置angular.json :

{
  "angularCompilerOptions": {
    "strictStandalone": true
  }
}

测试工具状态

自从我们在 Angular CLI 中引入实验性JestWeb Test Runner支持以来,我们继续评估该空间并收集开发人员的反馈。

在单元测试领域,我们相信真正的浏览器测试可以确保我们在测试和生产中拥有相同的环境。为了支持开发人员迁移到新的基于 esbuild 的构建器,在 v19 中,我们引入了 Karma 的开发人员预览支持,以通过设置builderMode选项来使用application构建器。这缩短了单元测试的构建时间,并允许用户更轻松地利用application构建器特定的功能(例如文件加载器),而不会破坏测试。

随着 Karma 被弃用,我们将在 2025 年上半年继续评估现有的测试运行程序,以选择我们将继续执行的默认建议。关注我们的博客X以获取公告和调查。

从一开始就确保安全

我们与 Google 的安全团队合作开发了开发者预览功能,用于根据index.html中的脚本自动生成基于哈希的严格内容安全策略

使用基于哈希的 CSP,浏览器会将每个内联脚本的哈希添加到 CSP。每个脚本都有一个与其关联的唯一哈希值。这将防止攻击者在您的页面上运行恶意脚本,因为浏览器要执行该脚本,其哈希值需要存在于 CSP 中。

目前, autoCSP在开发者预览版中可供选择加入。要在应用程序中使用它,请通过在项目内的security下将autoCSP属性设置为true来配置应用程序构建器angular.json 。

不断发展的反应性

过去两年 Angular 的核心主题是不断发展我们的反应系统。在版本 19 中,我们很高兴与大家分享一些新的免费 API 以及我们在之前版本中引入的一些基本反应性 API 的稳定性,例如输入、输出和视图查询。

inputs、outputs 和 view queries API 的稳定性

在过去的一年里,我们观察了开发人员如何使用新的输入、输出和视图查询 API,并且我们正在将它们升级到稳定!为了简化这些新 API 的采用,我们开发了示意图来转换您现有的输入、输出和视图查询:

ng generate @angular/core:signal-input-migration
ng generate @angular/core:signal-queries-migration
ng generate @angular/core:output-migration

请注意,与传统输入相比,信号输入是只读的,因此如果您要设置输入值,则可能需要手动迁移应用程序的部分内容。

要一次运行所有这些迁移,您可以使用联合别名:

ng generate @angular/core:signals

您可以在我们的文档中阅读有关inputsoutputsview queries更多信息。

通过语言服务实现代码现代化

为了让您能够轻松地将代码更新到最新的 API,我们引入了原理图和 Angular 语言服务之间的集成。

语言服务与原理图集成

当您将 Angular 语言服务和项目更新到 v19 时,您可以直接从代码编辑器将您的输入、查询等更新为最新的 API!

引入linkedSignals

开发人员反馈以及观察应用程序如何使用 Angular 信号时,我们看到了使用新原语更好地服务常见用例的机会。通常在 UI 中,需要可变状态来仍然跟踪一些更高级别的状态。例如,选择 UI 具有“当前选择”状态,该状态会随着用户做出选择而变化,但如果选项列表发生变化,也需要重置。新的linkedSignal原语创建一个可写信号,它捕获这种类型的依赖关系:

const options = signal(['apple', 'banana', 'fig']);

// Choice defaults to the first option, but can be changed.
const choice = linkedSignal(() => options()[0]);
console.log(choice()); // apple

choice.set('fig');
console.log(choice()); // fig

// When options change, choice resets to the new default value.
options.set(['peach', 'kiwi']);
console.log(choice()); // peach

linkedSignal清楚地表达了optionschoice之间的关系,而无需求助于effect用法。新的 API 有 2 种形式:一种是简化的(此处介绍),另一种是高级的,开发人员可以访问optionschoice的先前值。它还具有高级 API,允许更复杂的逻辑,例如只要用户的选择存在于新的选项列表中,就保留该选择。

这个新的 API 是实验性的,所以请尝试一下并告诉我们您的想法!

介绍 Resource

到目前为止,Angular 中的信号主要关注同步数据:在信号、计算值、输入、查询等中存储状态。在 Angular v19 中,我们通过引入新的实验resource() API。资源是参与信号图的异步依赖项。您可以将资源视为由三个部分组成:
1. 请求函数,用信号表达要发出的确切请求。例如, user资源可能会计算依赖于当前路由中的用户 ID 参数的请求。
2. 加载器,当请求发生变化时执行异步操作,最终返回一个新值。
3. 生成的Resource实例,它公开传达值(如果可用)以及资源当前状态(加载、已解决、出错等)的信号。

@Component(...)
export class UserProfile {
  userId = input<number>();

  userService = inject(UserService);

  user = resource({
    request: user,
    loader: async ({request: id}) => await userService.getUser(id),
  });
}

今天,我们将resource()作为独立的实验性 API提供,以便测试 API 并获得开发人员的早期反馈。随着时间的推移,我们期望逐渐将对资源的支持更深入地纳入 Angular 中(例如,作为解析器的形式纳入路由器中),作为应用程序中异步故事的关键部分。

因为现在许多 Angular 应用程序使用 RxJS 来获取数据,所以我们还将rxResource添加到@angular/core/rxjs-interop中,它从基于 Observable 的加载器创建资源。

Effects API 的状态

在过去的几个版本中,我们一直保持开发者预览版的effect ,以观察开发者如何使用它们。根据您的反馈,在 v19 之前,我们对effect时间进行了更改,以更好地服务于您的用例。您可以在我们的博客上阅读有关变更和 API 改进流程的更多信息。

作为新反应性 API 中的核心原语,我们希望花时间确保effect的语义正确。我们会将此 API 保留在开发者预览版中,以便在发现尚未考虑的用例时进行更改。

zoneless 状态

六个月前,我们向 Angular 引入了实验性无区域支持。从那时起,我们一直在迭代 API 并对其进行增强——添加对服务器端渲染的支持并改善测试体验。我们还与Google Fonts团队合作,使其应用​​程序实现无区域化并评估开发人员体验。结果以及向无区过渡的简易性超出了我们的预期,但在将此 API 移至开发人员预览版之前,我们仍希望进行一些改进。

2025 年,我们将继续改进 zoneless。与此同时,请确保在应用程序引导程序中尝试一下,并让我们知道您的体验!创建无区域项目的最简单方法是使用 Angular CLI:

ng new [project-name] --experimental-zoneless

感谢Angelo Parziale对社区的贡献

在现有应用程序中,您可以使用实验性的zoneless提供程序:

bootstrapApplication(App, {
  providers: [
    provideExperimentalZonelessChangeDetection()
  ]
});

接下来,确保从angular.json的 polyfills 部分中删除 zone.js 。

推进 Angular Material 和 CDK

今年早些时候,我们发布了稳定版本的 Material 3,这使得我们的材质组件能够通过Design Token支持的强大 Sass 主题 API 更加可定制。在 v19 中,我们引入了主题 API 的增强功能,使您可以更轻松地自定义组件!

增强的主题 API

通过 Material 3,我们使您能够使用特定于组件的 mixin创建自定义主题

@use '@angular/material' as mat;

@include mat.core();

$light-theme: mat.define-theme((
    color: (
      primary: mat.$violet-palette,
      tertiary: mat.$orange-palette,
      theme-type: light
    ),
    typography: Roboto,
    density: 0
  ));

html {
  // Apply the light theme by default
  @include mat.core-theme($light-theme);
  @include mat.button-theme($light-theme);
  @include mat.card-theme($light-theme);
  // and 27 more...
  ...
}

使用这种高度可定制的 API,您通常最终会为各个组件重复代码。为了简化自定义主题的创建,在 v19 中,我们启用了更具表现力的 API,允许您使用单个 mixin — mat.theme来声明自定义主题:

@use '@angular/material' as mat;

html {
  @include mat.theme((
    color: (
      primary: mat.$violet-palette,
      tertiary: mat.$orange-palette,
      theme-type: light
    ),
    typography: Roboto,
    density: 0
  ));
}

覆盖组件样式

要自定义各个组件的样式,您可以使用我们在 Sass 中提供的新覆盖 API:

@include mat.sidenav-overrides(
  (
    'content-background-color': purple,
    'container-divider-color': orange,
  )
);

上面的代码片段将分别将内容背景和内容分隔线颜色覆盖为purpleorange ,同时保留剩余设计令牌的原始值,尊重您配置的应用程序主题。

二维拖放

为了使 Angular CDK 更加强大,我们在 CDK 中开发了对二维拖放的支持,这是 GitHub 上 311👍 的一个非常受欢迎的功能请求。

以下是如何使用 CDK 的此功能的快速片段:

<div
  cdkDropList
  cdkDropListOrientation="mixed" <!-- specify mixed orientation -->
  ...>
  @for (item of mixedTodo; track item) {
    <div cdkDrag>
      {{item}}
      <mat-icon cdkDragHandle svgIcon="dnd-move"></mat-icon>
    </div>
  }
</div>

效果是这样的:

拖放演示

文档中了解更多信息。

支持选项卡重新排序

我们最近发布的另一个功能请求是支持使用 Angular CDK 进行选项卡重新排序(24 👍)。使用此功能,您可以轻松地使选项卡可拖动,Google Cloud Console 团队立即通过 Angular 和 CDK 将其引入BigQuery :

新的时间选择器组件

最受欢迎的功能请求之一是 Angular Material 的时间选择器组件,在 GitHub 上有超过 1.3k 个👍。我们没有立即实施它,因为没有严格的规范,但考虑到需求,我们创建了一个符合您的要求和可访问性标准的设计,并在 v19 中发布!

时间选择器组件的基本使用

还有更多!

除了我们在性能、反应性、开发人员体验和独立等主要主题上进行的重大改进之外,我们还进行了大量的生活质量改进,使构建 Angular 应用程序变得更加愉快!

报告独立组件中未使用的导入

报告独立组件中未使用的导入一直是最受欢迎的功能之一,有超过 150 个👍!

从 v19 开始,Angular CLI 将报告未使用的导入的警告。

类似这样的报错

打包的时候会报错,不信的大佬自己试试,Angular官方的文章没轻没重的,一个gif居然有7MB大小,简直了。

此外,Angular 语言服务将突出显示此类未使用的导入,并提供直接在 IDE 或文本编辑器中自动删除它们的功能。

要取消此检查,您可以更新angular.json :

{
  "angularCompilerOptions": {
    "extendedDiagnostics": {
      "checks": {
        "unusedStandaloneImports": "suppress"
      }
    }
  }
}

命令行环境变量声明

Angular CLI 存储库中请求最多的功能(超过 350 个👍)是能够在构建期间传递环境变量

从 v19 开始,您可以使用--define标志来实现此目的:

ng build --define "apiKey='$API_KEY'"
declare global {
  var apiKey: string;
}

await fetch(`/api/data?apiKey=${globalThis.apiKey}`);

模板中的局部变量@let(这东西老好用了)

多年来,我们收到了数百个功能请求,要求在模板中引入局部变量声明语法 (443 👍)。不幸的是,多年来我们没有最佳的语法结构来做到这一点。

通过内置控制流和可延迟视图的新块语法,我们设计了一个解决方案,可以满足开发人员对本地模板变量声明的需求。我们在开发者预览版中作为 Angular v18.1 的一部分提供了此功能。在观察开发人员如何使用这种新语法后,我们现在正在将其升级为稳定版本!

它可以与模板引用和异步管道完美配合:

<!-- Use with a template variable referencing an element -->
<input #name>

@let greeting = 'Hello ' + name.value;

<!-- Use with an async pipe -->
@let user = user$ | async;

随着最佳实践的不断发展,应用程序不断发展

与 Angular 和 Web 平台一起发展您的应用程序是我们的核心价值观之一。为了确保您的应用程序使用最新的 API 和最佳实践,我们进行了多项改进:

非常感谢 Angular 社区

作为开发人员,我们正在专门为像您这样的其他开发人员构建产品。如果没有 Angular 社区的大力支持和贡献,我们就不会走到今天。你们每个人在塑造 Angular 的未来方面都发挥着至关重要的作用。

您的反馈、开源包以及对聚会和会议的积极参与帮助我们让 Angular 每天变得更好。您在 StackOverflow、Discord、Reddit、Telegram 等平台上分享的知识可以为全世界的开发者提供帮助。

我们邀请您在线或本地加入这个充满活力的社区。仅今年一年,全球就举办了十场 Angular 会议,分别来自比利时德国印度以色列意大利肯尼亚马其顿波兰塞尔维亚美国。这些活动是与其他开发人员联系、了解最新进展并分享您的专业知识的绝佳机会。

如果您组织的 Angular 会议不在我们的列表中,请通过devrel@angular.io告知我们,以便我们进行宣传。

我们还要感谢过去两个主要版本之间帮助我们塑造 v19 的所有 247 名贡献者。

让我们继续学习、成长并使用 Angular 构建令人惊奇的事物!

迈向2025年!

在过去的一年里,我们努力开发作为此版本一部分的所有功能。我们还与数百名开发人员联系,收集您的反馈并了解我们如何在 2025 年为您提供最好的支持。我们正在仔细检查我们的笔记和开发人员满意度调查的结果,以验证我们的假设。

不断出现的几个核心主题围绕着 Angular 创作体验的现代化和重新思考我们的单元测试建议。我们计划明年初在这个领域进行彻底的研究,并与您分享我们的研究结果,以在做出任何决定之前收集反馈。与此同时,我们将继续完善我们的反应性 API,全面带来增量 DX 改进并不断发展 Angular 的性能,使您能够充满信心地构建 Web 应用程序!

感谢您帮助我们塑造 Angular 并迈向 2025 年! 🚀

原文链接:https://blog.angular.dev/meet-angular-v19-7b29dfd05b84

发表评论