上个月举办了 Angular 红色盾牌的 13 周年庆。曾几何时,AngularJS 是通过 JavaScript 框架满足日益增加的 web 开发体验需求的先锋队。今天在 v17 版本中,我们将以全新品牌形象、面向未来的功能设计,和大家一起重新定义应用性能和开发体验的新标准。
很高兴为大家介绍 v17 版本带来的新功能:
过去几个版本中,Angular 一直都在复兴之路上狂奔。我们通过改进诸如基于信号的响应式编程、hydration(译注:服务端渲染客户端激活的模式)、独立组件、组合指令等数十项特性,不断增强发展势头。尽管 Angular 的发展迅速,但其品牌形象却未能跟上这种快速的演变——自从 AngularJS 早期以来,它的品牌形象几乎没有变化。
今天,这个被数百万开发者使用和喜爱的框架将展现出全新的面貌,这一新形象也衬托了其面向未来的开发体验和卓越性能!
随着新品牌的推出,我们为 Angular 文档也打造了一个崭新的家园——angular.dev 网站。在这个全新的文档网站中,我们重新设计了结构,编写了新的指南,优化了内容,并构建了一个交互式的学习平台,使开发者能够按照自己的节奏在浏览器中深入学习 Angular 和 Angular CLI。
新的交互式的学习平台采用了 WebContainers 技术,因此开发者可以在任何现代的网络浏览器中充分使用 Angular CLI 的强大功能!
基于 WebContainers 的 Angular 交互式体验页面
今天,我们推出了 angular.dev 的体验版,并计划在 v18 版本中将其作为 Angular 的默认网站。
开发者可以在“angular.dev 的官方发布”这篇文章中了解更多关于 Angular 新品牌形象和以及 angular.dev 网站的信息。
现在,让我来把在 v17 版本中那些迫不及待想要告诉大家的新功能做个详细介绍!
为了提升开发体验,我们发布了一种新的块模板语法。基于该语法,只需使用简单、声明式的 API 即可实现强大的功能。在底层,Angular 编译器会将这种语法转换成高效的 JavaScript 指令,这些指令可以执行流程控制、懒加载等操作。
基于新的块模板语法,我们做了内置控制流的优化。通过用户调研,我们发现许多开发者在使用 *
ngIf、*
ngSwitch 和 *
ngFor 时经常面临重重困难。甚至包括我本人在内也是如此,虽然我个人从 2016 年就开始使用 Angular,且作为 Angular 团队成员也有 5 年之久,但在使用 *
ngFor 和 trackBy 时,依然需要查阅文档才能确保正确使用。在收集了社区、合作伙伴的反馈,并进行了用户体验调研之后,我们为 Angular 开发了一种新的内置控制流!
新的内置控制流带来了如下好处:
更贴近 JavaScript 语法,更符合开发者使用习惯,更直观,从而减少了查阅文档的需求
得益于更优化的类型收敛(type narrowing),类型检查得到了很好的改善
内置控制流是构建阶段核心处理的部分,除了大大减少运行时的消耗(甚至直接消失)之外,还将使你的应用程序包大小整体减少 30KB 之多,从而进一步提高应用的核心网络指标(Core Web Vital)的得分
无需额外导入,即可在模版中通过变量来使用
显著的性能提升,稍后我们会专门介绍
让我们来看一个在当前版本和 v17 版本中使用 *ngIf 的对比:
The user is logged in
The user is not logged in
使用内置 if 语句之后:
@if (loggedIn) {
The user is logged in
} @else {
The user is not logged in
}
与传统的 *ngIf 通过使用模块替换来实现 else 相比,能够直接编写 @else 内容是一个重大的简化。当前的控制流也使得 @else if 的支持变得非常简单,而这在之前的版本中这是无法实现的。
在 *ngSwitch 中,使用内置控制流改进后显现出来的易用性就更加突出了:
使用内置 switch 语句之后:
@switch (accessLevel) {
@case ('admin') { }
@case ('moderator') { }
@default { }
}
在新的控制流中,在 @switch 的条件分支上更好、更显著地实现了类型收敛,而这在老的 *ngSwitch 中是无法做到的。
内置循环是我最喜欢的更新之一,它在改善开发体验的同时,还将 Angular 的渲染速度提升到了一个新的级别。
下面是基础示例:
@for (user of users; track user.id) {
{{ user.name }}
} @empty {
Empty list of users
}
我们经常看到应用程序由于 *ngFor 缺少 trackBy 函数而产生的性能问题。因此,为了保证差异对比运算的性能,强制使用 track 函数,是新的 @for 循环语句的首要变更。此外,在使用上 @for 也更加简单,因为它只是一个表达式,而不是组件类中的一个方法。同时,内置的 @for 循环还通过一个可选的 @empty 块语法为空集合提供了快捷处理方式。
@for 语句使用了新的差异算法,相对于 *ngFor 指令,在实现上做了非常多的优化,这也使得其在执行速度上相对于社区框架基线提高了 90%。
内置 for 语句与 js-framework-benchmarks 中 *ngFor 的性能比较,数据来源:
https://krausest.github.io/js-framework-benchmark/current.html
今天,在 v17 的开发体验版中就可以体验内置控制流的全部功能!
内置控制流的设计目标之一,就是实现让开发者通过 CLI 命令就可以自动完成已有项目的完整迁移。因此,开发者可以在已有项目中,通过执行使用如下命令来完成自动迁移:
ng generate @angular/core:control-flow
我们已经和 JetBrains 进行了紧密合作,开发者在 JetBrains 的产品中可以使用内置控制流体验最新的语言服务。于此同时,我们也在和 Sosuke Suzuki 联系,以确保 Prettier 可以正确的支持 Angular 模板格式。
在处理内容预测上,内置的控制流与 *
ngIf、*
ngFor 和 *ngSwitch 还存在一些差异,我们将在接下来的几个月中努力解决这些问题。尽管如此,我们依然对内置控制流的实现和稳定性充满信心,所以你今天就可以尝试使用了!我们之所以将其保留在开发体验版本中,直到下一个主要版本才发布,是因为我们希望借此机会发现问题,从而有机会进一步提升开发者体验和修复一些可能存在的向后不兼容的问题。
现在,让我们来谈谈懒加载功能!基于新研发的块状语发糖,我们全新设计了一个强大的机制来实现延迟视图,从而使开发者的应用程序跑得更快。正如本文开篇所说,延迟视图将应用性能和开发者体验都提升到了一个新水平。这是因为它们开创性地实现了基于声明式的懒加载。
此组件树中左边的子树被懒加载
假设你有一个博客,你想要实现评论列表的懒加载。那么在当前的版本中,你需要使用 ViewContainerRef 来实现,同时还需要处理诸如:清理、加载异常、显示占位符等复杂逻辑。与此同时,还需要兼顾一些边界情况,因此代码会变得非常复杂,甚至无法调试和测试。
然而,在 v17 版本中,通过新的延迟视图功能,你只需要通过一行声明式的代码就可以实现评论列表及其所有传递依赖项的懒加载:
最令人难以置信的是,这一切都是在编译期间通过转换技术来实现的:Angular 抽象了延迟视图的全部复杂逻辑。编译期间,Angular 通过找到在 @defer 块中声明的组件、指令和管道,自动将其转为动态导入,同时还会自动管理目标组件的加载和状态切换的全过程。
当组件的懒加载是通过某个 DOM 元素进入视窗来触发时,通常需要结合 IntersectionObserver API 编写复杂逻辑来实现。
然而,Angular 却将 IntersectionObservers 的使用,变得只需要简单的添加一个延迟视图触发器即可实现!示例代码如下:
@defer (on viewport) {
} @placeholder {
}
在上面的例子中,Angular 首先渲染通过 @placeholder 声明的占位符。当
组件准备进入视窗中时,Angular 就会开始加载该组件。在加载完成之后,Angular 会立刻移除占位符并渲染该组件。
下面是包含处理加载过程和异常处理的示例代码:
@defer (on viewport) {
} @loading {
Loading…
} @error {
Loading failed :(
} @placeholder {
}
就是这样!Angular 为你处理了许多隐藏在幕后的复杂逻辑。
延迟视图支持的触发器非常多,例如:
on idle——当浏览器没有执行任何繁重任务时,触发延迟视图加载
on immediate——以不阻塞浏览器的方式,自动触发延迟视图加载
on timer(
)——通过定时器触发延迟视图加载
on viewport 和 on viewport(
[
)——viewport 触发器还支持设置一个锚点元素,当指定的锚点元素可见时,触发延迟视图加载
]
on interaction 和 on interaction(
[
)——允许你在用户与特定元素交互时触发延迟视图加载
]
on hover 和 on hover(
[
)——当用户鼠标悬停在元素上时触发延迟视图加载
]
when
——通过返回 promise,实现通过自定义条件触发延迟视图加载
延迟视图还提供了在渲染它们之前预先获取依赖项的能力。只需要在 defer 块中简单的添加一个 prefetch 语句即可为延迟视图添加预获取功能。所有和 viewport 相同类型的触发器,都支持添加预获取功能,示例代码如下:
@defer (on viewport; prefetch on idle) {
}
大家今天就可以在 v17 的开发体验版中体验延迟视图功能!如果想了解更多、更详细的关于延迟试图功能,请阅读这份指南。
延迟试图完全可以被正式使用,同时我们也强烈建议开发者尝试去使用该功能!该功能之所以依然在开发体验版中,是因为我们希望收集到更多的使用反馈。同时,和框架其他功能一样,我们会按照语义化版本的方式不断迭代该功能的 API,直到稳定为止。
目前,在服务端渲染时,针对延迟试图,会使用一个特殊的占位符取代。一旦框架完成应用加载且延迟试图被激活,那么延迟视图就会按照上文所描述的那样执行。
我们的下一步计划是探索 defer 中声明的内容在服务端渲染和客户端按需激活的能力。这将意味着,在被触发之前,客户端将不会下载延迟试图的代码。当延迟视图被触发时,Angular 将下载相关的 JavaScript 代码并仅对视图的这一部分进行水合处理。
还将会有更多令人兴奋的,结合 signals 实现的功能,敬请期待!
今天,我们还为开发者带来了基于交互式命令的 SSR(服务端渲染)和 SSG(静态网站生成 / 预渲染)功能,下面是使用 ng new 命令的示例:
这是一个我们一直想做的改变,但是首先我们想先保证 Angular 的 SSR 开发者体验的稳定性。
或者,你可以用以下方式在新项目中启用 SSR:
在过去的 6 个月里,我们看到数千个应用程序采用了 hydration。
今天,我们很高兴地宣布,hydration 正式从开发体验版中移除,且在所有使用了服务器端渲染的新应用中默认开启!
我们将 Angular 中的通用代码库存迁移移至 Angular CLI 代码库中,从而使服务器端渲染成为了我们工具产品中更加不可或缺的一部分。
因此,从今天开始,可以通过如下命令为历史项目添加混合渲染的支持:
此命令在为应用会生成服务端入口点、添加 SSR 能力和 SSG 构建能力的同时默认开启 hydration。@angular/ssr 提供了与 @nguniversal/express-engine(该库当前处于维护模式)等效的功能。如果你使用的是 express-engine,那么 Angular CLI 将会自动为你将代码更新为 @angular/ssr。
Virgin Media O2 通过将他们历史平台的混合渲染技术迁移至最新的 Angular 混合渲染技术,销售额增长了 112%。
通过将 NgOptimizedImage 与支持 DOM Hydration 的 Angular SSR 结合使用,其平台的 CLS(累计布局偏移)指标平均降低了 99.4%。
为了进一步增强开发人员体验,我们与云提供商密切合作,确保基于 Angular 的 SSR 应用可以丝滑的部署到他们的云平台上。
通过 Firebase 的最新发布的框架感知 CLI,开发者几乎不用任何配置,就可以将自己的 Angular 应用程序部署到该平台上。
firebase experiments:enable webframeworks
firebase init hosting
firebase deploy
框架感知 CLI 可以自动识别应用中用到的 SSR、i18n 和图像优化等功能,这使你能够经济且高效的在无服务器基础设施上为用户提供高性能的 web 应用服务。
对于那些拥有复杂 Angular monorepos 项目或偏爱原生工具链的人们,AngularFire 允许你使用 ng deploy 将应用部署到 Firebase:
ng add @angular/fire
ng deploy
为了支持边缘环境部署,我们在 Angular 的服务器端渲染中不但提供了 ESM 的支持,为 HttpClient 做服务端适配,还与 CloudFlare 合作简化了部署流程。
虽然从长远来看,为了提高 Angular 的 SSR 和 SSG 的性能,我们倾向于移除 DOM 的模拟和直接操作。但与此同时,在大多数应用的生命周期中,很难避免诸如实例化第三方库、测量元素大小等和 DOM 元素相关的交互行为。
为此,我们新增了两个生命周期 hooks:
afterRender — 注册一个回调函数,每次应用完成渲染时调用
afterNextRender — 注册一个回调函数,应用下次完成渲染时调用
这两个 hooks 只会在浏览器端才会被调用,因此开发者可以安心的在其中使用 DOM 操作逻辑。例如,如果你想实例化一个图表库,可以通过 afterNextRender 来实现:
@Component({
selector: 'my-chart-cmp',
template: `{{ ... }}
`,
})
export class MyChartCmp {
@ViewChild('chart') chartRef: ElementRef;
chart: MyChart|null;
constructor() {
afterNextRender(() => {
// 这里需要操作真实DOM,必须保证DOM已经具备了
this.chart = new MyChart(this.chartRef.nativeElement);
}, {phase: AfterRenderPhase.Write});
}
}
每个 hooks 都支持设置一个阶段值(如读取、写入),Angular 将使用该阶段值来择机执行回调,以减少布局抖动并提高性能。
vite 和 esbuild 为 ng server 和 ng build 助力
如果不是一开始就对 Angular CLI 的构建流程进行彻底的改造,那么 Angular 将不可能支持 SSR!
在 v16 的开发体验版中,我们就引入了基于 esbuild+Vite 的构建能力。从那时起,就有许多开发者和部分企业合作伙伴试用了该功能,数据报告显示他们的一些应用的
构建时间缩短了 67%!
今天,我们很高兴地宣布,新的应用程序构建器正式从开发体验版中毕业,并且为所有新应用程序默认开启!
此外,我们还升级了混合渲染场景的构建流程。
在 SSR 和 SSG 应用程序中,ng build 的执行速度提高了 87%,ng serve 的实时编辑反馈速度提高了 80%。
基于 esbuild+vite 的新构建流程和基于 webpack 的老构建流程的性能对比
在未来的 minor 版本中,我们将为历史项目升级混合渲染(使用 SSG 或 SSR 进行客户端渲染)提供自动脚本。如果你今天就想试用新的应用程序构建流程,可以查看这份升级指南。
去年,我们向大家演示了在 Angular DevTools 中进行依赖注入调试的功能。在过去的几个月里,我们实现了全新调试 API,通过这些 API,开发者可以直接插入框架的运行时进行注入树的检查。
基于这些 API,我们构建了一个检查注入依赖的用户界面,通过此界面开发者可以预览如下信息:
基于组件监听的组件的依赖关系
注入树和依赖解析路径
每个注入的提供者信息
你可以在下面的动画中快速预览这些功能。也可以在 angular.io 上了解有关 Angular DevTools 的更多信息。
接下来我们将继续打磨注入层次结构、提供者信息和路径解析等方面的视图 UI,为开发者提供更好的可视化能力。
在经历一年半的持续收集独立组件、指令和管道的使用反馈并不断完善了它们的 DevEx 之后,我们有信心立刻在所有新应用程序中启用它们。所以,现在所有的 ng generate 命令都将构建独立的组件、指令和管道。
与此同时,我们还重新审视了 angular.io 和 angular.dev 的全部文档,从而确保了一致的学习体验、开发实践和建议。
虽然,在未来的一段时间内,我们将继续保留 NgModules,但当看到新的独立 API 的好处之后,我们依然强烈建议你在项目中逐步执行迁移动作。同时,我们还提供了一个迁移脚本,该脚本可以为你自动完成大部分工作:
ng generate @angular/core:standalone
有关更多信息,请查看我们的迁移指南。
Angular 新的基于信号的响应系统是我们在该框架中所做的重大转变之一。为了确保与基于 Zone.js 的变更检测的向后兼容性和互操作性,我们一直在努力制作原型并设计推进的线路。
今天,我们很高兴地宣布 Angular 的基于 Signals 的响应式实现从开发体验版中毕业。
目前,我们将把 effect 函数继续保留在开发者体验中,以便我们可以进一步迭代它的语义。
在接下来的几个月中,我们将开始逐步推出基于信号的输入、视图查询等功能。到明年 5 月,在 Angular v18 中,我们将提供更多的功能来进一步改善开发人员使用 Signals 的体验。
我们正在持续试验 Jest,以并确保我们可以构建一个高性能、灵活且直观的解决方案,从而更好地满足开发人员的需求。与此同时,我们还开始尝试 Web Test Runner,并为初始实施提供了一个开放的 PR。在不久的将来,我们可能会优先关注 Web Test Runner,以解放那些渴望摆脱 Karma 的项目。
在为 Angular Material 引入设计令牌的重构中,我们一直与谷歌的 Material Design 团队努力合作。该设计系统将为组件提供更多的自定义选项并启用 Material 3 的支持。虽然我们还没有准备好在 v17 中支持设计令牌和 M3,但预计很快会在 v17 的某个小版本中会得到支持。
在 2022 年第四季度,我们宣布了推出基于 MDC 的新 Angular Material 组件,并弃用具有相同功能但 DOM 结构和样式不同的旧组件。我们在 v15 中弃用了旧组件,并将在 v17 中将其移除。虽然它们不再属于 Angular Material v17 包的一部分,但你仍然可以将应用程序更新到 Angular v17 的同时依然使用 v16 版本的 Angular Material 包。在 v18 之前,此兼容将一直支持,但在此之后新版本的 Angular 将不再兼容 Angular Material v16。当然,为了防止你暂时无法执行项目迁移,我们与 HeroDevs 进行了合作,他们将提供持续的付费支持。
除了所有这些前瞻性的功能,我们还发布了一系列针对开发人员体验的小型增强功能!