Angular 17 来了,性能大幅提升!
11 月 8 日,Angular 17 正式发布,该版本带来了很多重要更新,包括:
引入了可延迟的视图,将性能和开发者体验提升到新的高度。
内置控制流循环使运行速度在公共基准测试中提高了高达90%。
混合渲染和客户端渲染的构建速度分别提高了87%和67%。
全新的外观体现了 Angular 未来的功能。
全新的互动学习之旅,带来更好的用户体验。
面向未来的品牌形象
经过过去几个版本的快速发展,Angular 已经迎来了全新的面貌。凭借基于信号的反应性、水化、独立组件、指令组合等创新功能,它已经得到了数百万开发人员的实战检验和广泛喜爱。
尽管Angular发展迅速,但其品牌形象一直未能跟上——自AngularJS早期以来几乎保持不变。今天,这个备受瞩目的框架焕然一新,以反映其前瞻性的开发者体验和卓越性能,进一步引领 Web 开发的新潮流。
Angular 全新的 Logo 如下:
面向未来的文档
除了新品牌,Angular 团队还为 Angular 官方文档开发了一个全新的主页:angular.dev。针对新的文档网站,Angular 团队设计了一个全新的结构、提供了全新的指南、改进了内容质量,并构建了一个互动学习平台,以便让开发者在浏览器中按照自己的进度学习 Angular 和 Angular CLI。
新的交互式学习体验由 WebContainers 提供支持,可以在任何现代 Web 浏览器中使用 Angular CLI 的强大功能!
今天,正式推出 angular.dev 的 Beta 预览版,并计划在 v18 版本中将其设为 Angular 的默认官方网站。
下面来看看 Angular 17 中新增的功能。
内置控制流
为了提升开发者的体验,Angular 17 引入了新的块模板语法,它通过简单且声明式的 API 提供了强大的功能。在幕后,Angular 编译器将此语法转化为高效的 JavaScript 指令,从而实现了控制流、延迟加载等更多操作。
为了解决开发人员在使用 *ngIf
, *ngSwitch
, 和 *ngFor
时的困扰,v17 中采用了新的块语法以优化内置控制流。在经过用户研究和收集社区与合作伙伴的反馈后,决定为 Angular 创建一个内置的控制流,致力于提高开发体验!
内置控制流可以:
更符合人体工程学的语法,让代码更加直观,减少文档查找的需求。
通过更优化的类型检查,提供更好的类型安全性。
该功能主要在构建时起作用,最大限度地减少了运行时占用空间,从而有可能将包大小减少30kb,同时提高 Core Web Vital 得分。
无需额外导入,该功能自动适用于模板。
条件语句
先来使用*ngIf
来实现条件语句
<div *ngIf="loggedIn; else anonymousUser">
用户已登录
</div>
<ng-template #anonymousUser>
用户未登录
</ng-template>
使用内置 if 语句,此条件将如下所示:
@if (loggedIn) {
用户已登录
} @else {
用户未登录
}
与传统的 *ngIf
相比,内置 if
语句的 @else
子句提供了更加简洁的条件判断。此外,当前的控制流使得 @else if
的实现变得轻而易举,这在传统的 *ngIf
中是不可能的。
在*ngSwitch
中,改进的人体工程学表现得更为明显:
<div [ngSwitch]="accessLevel">
<admin-dashboard *ngSwitchCase="admin"/>
<moderator-dashboard *ngSwitchCase="moderator"/>
<user-dashboard *ngSwitchDefault/>
</div>
通过内置控制流,它变成了:
@switch (accessLevel) {
@case ('admin') { <admin-dashboard/> }
@case ('moderator') { <moderator-dashboard/> }
@default { <user-dashboard/> }
}
新的控制流可以在 @switch
中的各个分支中实现更好的类型缩小,这在 *ngSwitch
中是不可能的。
内置for循环
新版本还引入了内置的 for 循环,它极大地改善了开发者体验,并将 Angular 的渲染速度提升到了全新的高度!
其基本语法是:
@for (user of users; track user.id) {
{{ user.name }}
} @empty {
用户列表为空
}
我们经常遇到由于 *ngFor
中缺少 trackBy
函数而导致的性能问题。@for
的不同之处在于,为了确保快速的比较性能,track 是必需的。此外,由于它只是一个表达式而不是组件类中的方法,因此使用起来更加简单。内置的 @for
循环还提供了一个快捷方式,可以轻松遍历零个项目的集合,这是通过可选的 @empty
块实现的。
@for
语句使用了新的 diff 算法,与 *ngFor
相比具有更优化的实现,这使得社区框架基准测试的运行时间提高了 90%!
ng generate @angular/core:control-flow
可延迟的视图
利用新开发的块语法,创建了一个强大而高效的新机制,可以让应用运行得更快。可延迟的视图通过前所未有的便捷性,实现了声明性且强大的延迟加载,从而将性能和开发者体验提升到了新的高度。
假设有一个博客页面,希望实现用户评论列表的延迟加载。当前需要使用 ViewContainerRef
,同时还要处理各种复杂性,如清理、错误管理、占位符显示等。处理这些边角情况可能会涉及一些复杂的代码,给测试和调试带来困难。
新的可延迟视图只需一行声明性代码就可以延迟加载注释列表及其所有传递依赖项:
@defer {
<comment-list />
}
这一切都是通过编译时的转换实现的:Angular 找到了 @defer
块内使用的组件、指令和管道,将所有复杂性抽象化,生成动态导入,并管理加载和状态切换过程。
使用IntersectionObserver
API来实现视口进入时的延迟加载组件涉及复杂的逻辑。然而,Angular 简化了这个过程,只需添加一个可延迟的视图触发器即可!
@defer (on viewport) {
<comment-list />
} @placeholder {
<img src="comments-placeholder.png">
}
在上面的示例中,Angular 首先渲染占位符块的内容。当它在视口中可见时, 组件就会开始加载。加载完成后,Angular 会删除占位符并渲染组件。
还有用于加载和错误状态的块:
@defer (on viewport) {
<comment-list/>
} @loading {
Loading…
} @error {
Loading failed :(
} @placeholder {
<img src="comments-placeholder.png">
}
Angular 为开发者管理了大量的复杂性。
可延迟视图提供了更多触发器:
on idle
— 在浏览器闲置时延迟加载块on immediate
— 自动开始延迟加载,不会阻塞浏览器on timer(time)
— 使用计时器延迟加载,时间可自定义on viewport
和on viewport(ref)
— viewport 还允许为锚元素指定一个引用。当锚元素可见时,Angular 会延迟加载组件并渲染它on interaction
和on interaction(ref)
— 允许在用户与特定元素交互时启动延迟加载on hover
和on hover(ref)
— 当用户悬停元素时触发延迟加载when expr
— 允许通过返回一个 promise 的表达式来指定自定义条件
可延迟视图还提供了在渲染依赖项之前预取依赖项的能力。添加预取就像向 defer
块添加预取语句一样简单,并且支持所有相同的触发器。
@defer (on viewport; prefetch on idle) {
<comment-list />
}
改进的混合渲染体验
该版本在ng new
中加入了提示,使服务端渲染(SSR)和静态站点生成(SSG或预渲染)更易于使用。
或者,可以通过以下方式在新项目中启用 SSR:
ng new my-app --ssr
新的 @angular/ssr 包
已经将 Angular 通用存储库移至 Angular CLI 存储库,使服务端渲染成为 Angular 工具产品中不可或缺的一部分!
从今天开始,要向现有应用添加混合渲染支持,可以运行以下命令:
ng add @angular/ssr
此命令将生成服务器入口点,并自动添加 SSR 和 SSG 构建功能,同时默认启用 hydration。@angular/ssr
提供了与当前处于维护模式的 @nguniversal/express-engine
相同的功能。如果你的项目正在使用 express-engine,Angular CLI 将自动将代码更新为 @angular/ssr
。
通过将 NgOptimizedImage 与带有 DOM Hydration 的 Angular SSR 结合使用,累积布局偏移平均减少了 99.4%。
使用 SSR 部署应用
为了进一步增强开发人员体验,Angular 团队与云提供商密切合作,以实现顺利部署到他们的平台。
Firebase 现在将通过其新的框架感知CLI的早期预览,以近乎零配置自动识别和部署 Angular 应用。
firebase experiments:enable webframeworks
firebase init hosting
firebase deploy
框架感知的 CLI 可识别 SSR、i18n、图像优化等的使用,使开发者能够在经济高效的 serverless 基础设施上提供高性能的 Web 应用。
对于那些拥有复杂 Angular monorepos 或只是喜欢原生工具的人,AngularFire 允许使用 ng deploy
部署到 Firebase:
ng add @angular/fire
ng deploy
为了实现在边缘工作站上的部署,启用了 Angular 服端渲染的ECMAScript模块支持,引入了一个用于HttpClient
的fetch后端,并与 CloudFlare 合作简化了这一过程。
新的生命周期 Hooks
为了提高 Angular 的 SSR 和 SSG 的性能,从长远来看,Angular 团队希望摆脱 DOM 模拟和直接 DOM 操作。同时,在大多数应用的生命周期中,它们需要与元素交互以实例化第三方库、测量元素大小等。
为了实现这一点,Angular 团队开发了一组新的生命周期挂钩:
afterRender
— 注册一个回调,每次应用程序完成渲染时调用afterNextRender
— 注册一个回调,以便在应用程序下次完成渲染时调用
只有浏览器会调用这些 Hooks,这样就能够将自定义 DOM 逻辑安全地直接插入到组件中。例如,如果想实例化一个图表库,可以使用 afterNextRender
:
@Component({
selector: 'my-chart-cmp',
template: `<div #chart>{{ ... }}</div>`,
})
export class MyChartCmp {
@ViewChild('chart') chartRef: ElementRef;
chart: MyChart|null;
constructor() {
afterNextRender(() => {
this.chart = new MyChart(this.chartRef.nativeElement);
}, {phase: AfterRenderPhase.Write});
}
}
每个钩子都支持一个“相位值”(例如读取、写入),Angular 使用这个相位值来合理安排回调的执行时间,从而减少页面布局的频繁变化,提高整体性能。
新项目默认使用 Vite 和 esbuild
在v16版本中,首次引入了 esbuild 和 Vite 驱动的构建体验作为开发预览。自此,许多开发人员进行了尝试,一些企业合作伙伴反馈称他们的一些应用的构建时间缩短了67%!在 v17 中,新应用的构建器已经从开发预览阶段正式推出,并默认应用于所有新应用!
此外,在使用混合渲染时,更新了构建管道。通过使用SSR和SSG,ng build
的构建速度提高了87%,ng serve
的编辑刷新循环速度加快了80%。
在未来的次要版本中,将提供原理图,以使用混合渲染(使用 SSG 或 SSR 进行客户端渲染)自动迁移现有项目。
DevTools 中的依赖注入调试
去年,Angular 团队展示了 Angular DevTools 中依赖注入调试功能的预览。在过去的几个月里,实现了全新的调试 API,能够插入框架的运行时并检查注入器树。
基于这些 API,构建了一个检查用户界面,可以预览:
组件检查器中组件的依赖关系
注入器树和依赖关系解析路径
单个注入器内标明的供应商