Angular17
上个月是Angular诞生13周年。Angular 是新一波 JavaScript 框架的起点,旨在支持对丰富 Web 体验日益增长的需求。今天,我们凭借新的外观和一系列前瞻性功能,通过版本 17 带领大家走向未来,为性能和开发人员体验设定新标准。
在 v17 中,我们很高兴地介绍:
- 可延迟视图将性能和开发人员体验提升到一个新的水平
- 在公共基准测试中,通过内置控制流循环,运行时间提高了 90%
- 混合渲染的构建速度提高了 87%,客户端渲染的构建速度提高了 67%
- 全新的外观反映了 Angular 的未来特征
- 全新的互动学习之旅
- ...以及许多其他功能和改进!
面向未来的身份
在过去的几个版本中,Angular 的复兴一直在全力进行。我们一直在通过基于信号的反应性、独立组件、指令组合和许多其他功能等改进来取得进展。尽管 Angular 发展迅速,但它的品牌却未能跟上——从 AngularJS 早期以来,它几乎一模一样。
今天,您喜爱的、经过数百万开发者考验的框架焕然一新,反映了其面向未来的开发者体验和性能!
面向未来的文档
与新品牌一起,我们还为 Angular 文档开发了一个新家-angular.dev。对于新的文档网站,我们采用了新的结构、新的指南、改进的内容,并构建了一个交互式学习之旅平台,让您可以直接在浏览器中按照自己的节奏学习 Angular 和 Angular CLI。
新的交互式学习体验由 WebContainers 提供支持,让您可以在任何现代 Web 浏览器中使用 Angular CLI 的强大功能!
我们将推出 angular.dev 的 Beta 预览版,并计划将其设为 v18 中 Angular 的默认网站.您可以在“Angular.dev 公告”中了解有关 Angular 新外观和 angular.dev 的更多信息.
现在让我们深入了解 v17 的功能。
内置控制流程
为了改善开发人员体验,我们发布了新的块模板语法,通过简单的声明性API为您提供强大的功能。在底层,Angular 编译器将语法转换为高效的 JavaScript 指令,可以执行控制流、延迟加载等。我们使用新的块语法来实现优化的内置控制流。在进行用户研究后,我们发现许多开发人员都在 *ngIf、*ngSwitch 和 *ngFor 上苦苦挣扎。从 2016 年开始使用 Angular 并在过去 5 年里成为 Angular 团队的一员,我个人仍然需要查找 *ngFor 和 trackBy 的语法。在收集了社区、合作伙伴的反馈并进行用户体验研究后,我们为 Angular 开发了一个新的内置控制流程!
内置控制流程可以:
- 更接近 JavaScript的语法,因此更直观,需要更少的文档查找
- 由于更优化的类型缩小,会有更好的类型检查
- 这是一个主要存在于构建时的概念,它可以减少运行时占用空间,从而可以将您的包大小减少多达 30 KB,并进一步提高您的 Core Web Vital 分数
- 它会自动在您的模板中可用,无需额外导入
- 我们稍后会介绍显著的性能改进
条件语句
让我们看一下与 *ngIf 的并排比较:
<div *ngIf="loggedIn; else anonymousUser">
The user is logged in
</div>
<ng-template #anonymousUser>
The user is not logged in
</ng-template>
使用内置 if 语句,此条件将如下所示:
@if (loggedIn) {
The user is logged in
} @else {
The user is not logged in
}
与遗留 *ngIf 替代方案的 else 子句相比,能够直接为 @else 提供内容是一个重大简化。当前的控制流还使得 @else if 变得微不足道,这在历史上是不可能的。
*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 {
Empty list of users
}
我们经常在应用中看到由于*ngFor中缺少trackBy功能而导致的性能问题。@for的一些不同之处在于,跟踪是强制性的,以确保快速差分性能。此外,它更容易使用,因为它只是一个表达式,而不是组件类中的方法。内置的@for循环也有一个可选的@empty块的快捷方式,它可以在for循环为空时进行显示。
@for语句使用了一种新的差分算法,与*ngFor相比,它的实现更加优化,这使得它在社区框架基准测试中的运行速度提高了90% !
试一试
内置控制流现已在 v17 的开发者预览版中提供! 内置控制流的设计目标之一是实现完全自动化的迁移。要在现有项目中尝试它,请使用以下迁移:
ng generate @angular/core:control-flow
下一步是什么?
你已经可以在最新的语言服务中使用内置的控制流,我们与JetBrains密切合作,在他们的产品中提供更好的支持。我们还与来自Prettier的Sosuke Suzuki联系,以确保Angular模板的正确格式。与*ngIf、ngFor和ngSwitch相比,内置控制流处理内容投影的方式仍然存在一些差异,我们将在接下来的几个月里进行改进。除此之外,我们对内置控制流的实现和稳定性充满信心,所以您今天可以尝试一下!我们希望在下一个主要版本发布之前保持开发者预览,这样我们就可以为潜在的向后不兼容修复打开大门,以防我们找到进一步增强开发者体验的机会。
可推迟的视图
现在让我们来讨论一下延迟加载的未来! 利用新的块语法,我们开发了一种新的、强大的机制,您可以使用它来使您的应用程序更快。 在这篇博客文章的开头,我说过可延迟视图将性能和开发人员体验带到了一个新的水平,因为它们支持声明性和强大的延迟加载,具有前所未有的人机工程学。
我们推迟了左子树加载的组件树
假设您有一个博客,并且希望延迟加载用户评论列表。 目前,您必须使用ViewContainerRef,同时还要管理清理、管理加载错误、显示占位符等的所有复杂性。处理各种不同的情况可能会导致一些不平凡的代码,这将很难测试和调试。
新的可延迟视图允许您使用一行声明性代码延迟加载注释列表及其所有传递依赖项:
@defer {
<comment-list />
}
最令人难以置信的是,这一切都是通过编译时转换实现的:Angular通过查找@DEFER块中使用的组件、指令和管道来抽象所有的复杂性,生成动态导入,并管理加载和状态之间的切换过程。
当某个 DOM 元素进入视口时开始延迟加载组件涉及许多更重要的逻辑和 IntersectionObserver API。 Angular 使使用 IntersectionObservers 就像添加可延迟视图触发器一样简单!
@defer (on viewport) {
<comment-list />
} @placeholder {
<!-- A placeholder content to show until the comments load -->
<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>)
视口还允许指定锚元素的引用。当锚元素可见时,Angular 将延迟加载组件并渲染它on interaction
和on interaction(<ref>)
,使您能够在用户与特定元素交互时启动延迟加载on hover
和on hover(<ref>)
,当用户悬停元素时触发延迟加载when <expr>
,使您能够通过返回promise的表达式指定您自己的条件
可延迟视图还提供了在渲染依赖项之前预取依赖项的能力。添加预取就像向 defer 块添加预取语句一样简单,并且支持所有相同的触发器。
@defer (on viewport; prefetch on idle) {
<comment-list />
}
今天,可延迟视图在 v17 的开发者预览版中可用!了解有关本指南中该功能的更多信息。
下一步是什么
可推迟的观点已经准备好使用,我们强烈鼓励您尝试一下! 我们将它们保留在开发人员预览中的原因是,这样我们就可以收集更多的反馈,并在API界面中引入更改,直到我们像框架的其余部分一样,将它们锁定为遵循语义版本控制。
目前,服务器端渲染将渲染指定的占位符。一旦框架加载应用程序并对其进行水合,可延迟视图将按照我们上面描述的方式工作。
作为下一步,我们将探索在服务器上呈现延迟块中的内容,并在客户机上启用部分水合作用。 在这种情况下,客户端不会下载延迟视图的代码,直到触发器请求它。 此时,angular将下载相关的js,并只更新视图的这一部分。
还将有许多令人兴奋的信号互操作性,敬请期待!
改进的混合渲染体验
今天,我们通过新的提示使服务器端渲染(SSR)和静态站点生成(SSG或预渲染)更接近开发人员:
在新的angular应用程序中提示输入SSR和SSG
这是我们很长一段时间以来一直想要做出的改变,但首先我们想要对Angular的SSR开发经验充满信心。
或者,您可以通过以下方式在新项目中启用SSR:
这是我们一直想要做的改变,但首先我们想要对Angular的SSR开发者体验有信心。
或者,您可以通过以下方式在新项目中启用SSR:
ng new my-app --ssr
在过去的6个月里,我们看到成千上万的应用程序采用了水合作用。今天,我们很高兴地宣布,水合作用已经退出了开发者预览,并在所有使用服务器端渲染的新应用程序中默认启用!
新的@angular/ssr包
我们将angular通用存储库移动到了angular CLI存储库,并使服务器端呈现成为我们的工具产品中更不可或缺的一部分!
从今天开始,为你现有的应用程序添加混合渲染支持:
ng add @angular/ssr
此命令将生成服务器入口点,添加SSR和SSG构建功能,并在默认情况下启用水合作用。 @angular/ssr提供了与当前处于维护模式的@nguniversal/express-engine等效的功能。 如果您使用的是express-engine,Angular CLI会自动将您的代码更新为@angular/ssr。
Virgin Media O2在从其传统平台转移到最新的angular混合渲染解决方案后,销售额增长了112%。通过使用NgOptimizedImage和Angular SSR与DOM水合作用,累积布局偏移平均减少99.4%。
使用SSR部署应用程序
为了进一步增强开发人员体验,我们与云提供商密切合作,使其能够顺利部署到他们的平台。
FireBase现在将通过其支持框架的新CLI的早期预览,以接近零的配置自动识别和部署您的angular应用程序。
firebase experiments:enable webframeworks
firebase init hosting
firebase deploy
支持框架的CLI识别SSR、I18N、映像优化等使用,使您能够在经济高效的无服务器基础设施上提供高性能的Web应用程序。
对于那些使用复杂的Angular单节点或更喜欢本地工具的人来说,AngularFire允许使用ng deploy部署到Firebase:
ng add @angular/fire
ng deploy
为了能够部署到edge worker,我们在Angular的服务器端渲染中启用了ECMAScript模块支持,为HttpClient引入了一个fetch后端,并与CloudFlare一起简化了这个过程。
新的生命周期
为了提高Angular的SSR和SSG的性能,从长远来看,我们希望远离DOM仿真和直接的DOM操作。与此同时,在大多数应用程序的生命周期中,它们需要与元素进行交互,以实例化第三方库,测量元素大小等。
为了实现这一点,我们开发了一组新的生命周期钩子:
- afterRender 注册一个回调,以便在每次应用程序完成渲染时调用
- afterNextRender 注册一个回调,以便下次应用程序完成渲染时调用
只有浏览器会调用这些钩子,这使您能够将自定义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});
}
}
每个钩子都支持一个相位值(比如read、write), Angular会用它来调度回调,以减少布局混乱并提高性能。
Vite和esbuild是新项目的默认设置
Vite和esbuild为ng serve和ng build命令提供支持
如果没有我们在Angular CLI的构建管道中做的根本改变,我们不可能从一开始就在Angular中启用SSR !
在v16中,我们介绍了esbuild和Vite驱动的构建体验的开发者预览版。从那以后,许多开发者和一些企业合作伙伴都对它进行了试验,报告说他们的一些应用程序的构建时间缩短了67% !今天,我们很高兴地宣布,新的应用程序构建器从开发者预览版毕业,并默认为所有新应用程序启用!
此外,我们在使用混合渲染时更新了构建管道。使用SSR和SSG,您可以观察到ng构建速度提高了87%,ng服务的编辑刷新循环速度提高了80%。
新的esbuild + vite构建管道与基于webpack的传统管道的比较
在未来的小版本中,我们将发布原理图,以使用混合渲染(使用SSG或SSR的客户端渲染)自动迁移现有项目。如果您想今天测试新的应用程序构建器,请查看我们文档中的指南。
DevTools中的依赖注入调试
去年,我们预览了Angular DevTools中的依赖注入调试功能。在过去的几个月里,我们实现了全新的调试api,允许我们插入框架的运行时并检查注入器树。
基于这些api,我们构建了一个检查用户界面,允许您预览:
- 组件检查器中组件的依赖关系
- 注入器树和依赖解析路径
- 在单个注入器中声明的提供商
下一步,我们将优化UI,更好地可视化注入器层次结构、提供商及其解析。
从一开始就是独立的api
在过去一年半的时间里,我们收集了对独立组件、指令和管道的反馈,并完善了它们的DevEx,我们有信心从一开始就在所有新应用中启用它们。所有的ng generate命令现在都将支撑独立的组件、指令和管道。
除此之外,我们还重新构建了angular的整个文档。angular.io和angular.dev确保一致的学习经验、开发实践和建议。
在可预见的未来,我们会继续使用ngmodule,但看到新的独立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的项目的阻碍。
Material 3的下一步
我们一直在努力与Google的material设计团队一起重构Angular material的内部,以纳入设计令牌,这个系统将为组件提供更多的自定义选项,并启用material 3支持。虽然我们还没有准备好在v17中发布设计令牌和M3支持,但我们希望很快在v17的小版本中发布这些功能。
在2022年第四季度,我们宣布了新的基于mdc的Angular Material组件,并弃用了具有相同功能但DOM结构和样式不同的遗留组件。我们不赞成在v15中删除在v17中的遗留组件。即使它们不再是Angular Material v17包的一部分,你仍然可以将你的应用更新到Angular v17,并使用v16的Angular Material包。在v18之前,这将是一个选项,在v18之后,Angular Material v16将不再与新版本的Angular兼容。我们还与HeroDevs的合作伙伴合作,如果你还不能执行迁移,他们将提供永久的付费支持。
实验性视图转换支持
视图转换 API可在更改 DOM 时实现平滑转换。在 Angular 路由器中,我们现在通过该withViewTransitions
功能提供对此 API 的直接支持。使用此功能,您可以使用浏览器的本机功能在路线之间创建动画过渡。
您现在可以通过在引导期间在路由器的提供程序声明中配置此功能来将此功能添加到您的应用程序中:
bootstrapApplication(App, {
提供者: [
ProvideRouter(routes, withViewTransitions()),
]
});
withViewTransitions
接受带有 property 的可选配置对象onViewTransitionCreated
,这是一个为您提供一些额外控制的回调:
- 决定是否要跳过特定动画
- 向文档添加类以自定义动画并在动画完成时删除这些类
- ETC。
图像指令中的自动预连接
Angular 图像指令现在会自动为您作为参数提供给图像加载器的域生成预连接链接。如果图像指令无法自动识别源并且未检测到 LCP 图像的预连接链接,它将在开发过程中发出警告。
在图像指令指南中了解有关此功能的更多信息。
延迟加载动画模块
此功能可以使您的初始捆绑包(压缩后的 16KB)减少 60KB。社区贡献者Matthieu Riegler提出并实现了一项功能,允许您通过异步提供程序函数延迟加载动画模块:
从“@angular/platform-browser/animations-async”导入{provideAnimationsAsync};
bootstrapApplication(RootCmp, {
提供者: [provideAnimationsAsync()]
});
输入值变换
常见的模式是具有接收布尔输入的组件。然而,这对如何将值传递给此类组件设置了限制。例如,如果我们对 Expander 组件有以下定义:
@Component({
standalone: true,
selector: 'my-expander',
template: `…`
})
export class Expander {
@Input() expanded: boolean = false;
}
我们尝试将其用作:
<my-expander expanded/>
您将收到“字符串不可分配给布尔值”的错误。输入值转换允许您通过配置输入装饰器来解决此问题:
@Component({
standalone: true,
selector: 'my-expander',
template: `…`
})
export class Expander {
@Input({ transform: booleanAttribute }) expanded: boolean = false;
}
作为字符串的 Style 和 styleUrls
Angular 组件支持每个组件多个样式表。然而,绝大多数情况下,当我想要设置组件的样式时,我会创建一个数组,其中包含指向内联样式或引用外部样式表的单个元素。 一项新功能使您可以切换:
@Component({
styles: [`
...
`]
})
...@Component({
styleUrls: ['styles.css']
})...
……更简单、更符合逻辑:
@Component({
styles: `
...
`
})
...@Component({
styleUrl: 'styles.css'
})
...
当您使用数组时,我们仍然支持多个样式表。这更符合人体工程学,更直观,并且与自动格式化工具配合使用效果更好。
社区示意图
为了支持社区原理图的开发,我们提供了一些实用方法作为@schematics/angular/utility
. 现在,您可以将表达式直接导入到 Angular 应用程序的根目录中,并将提供程序添加到 Angular 应用程序的根目录中,以及向package.json
.
您可以在文档中的原理图指南中了解更多信息
用 Angular 构建未来
在过去的六个月里,我们一直在继续 Angular 的复兴,发布了一些功能,以提供更好的开发人员体验和性能。今天,我们很高兴在 Angular 更新的品牌和angular.dev 的学习体验中体现出这种势头。
在下一个发布周期中,预计 Angular 基于信号的反应性、混合渲染和学习之旅将发生大量演变。
我们很荣幸能够成为您使用 Angular 构建未来的旅程的一部分!谢谢你!