在之前的 Angularv15 中,Angular 团队通过将独立 API 从开发者预览版降级至稳定版,在 Angular 的繁难性和开发者体验方面达到了一个重要的里程碑。现在,Angular 将持续这一改良的势头,公布了自 Angular 最后推出以来最大的一次版本更新;在 Reactivity、服务器端渲染和工具方面获得了微小的飞跃。
一,从新思考响应式Reactivity
作为 v16 版本的一部分,Angular 带来了全新的 Reactivity 模型的开发者预览,它为性能和开发者体验带来了显著的改良。齐全向后兼容并可与以后零碎互操作的,并且提供了如下的一些性能:
- 通过缩小变化检测过程中的计算次数,进步运行时的性能。
- 为 Reactivity 带来了更简略的 mental 模型,使其分明地理解视图的依赖性和通过应用程序的数据流。
- 启用细粒度的 Reactivity,在将来的版本中,它将容许咱们只查看受影响组件的变动。
- 在将来的版本中,通过应用 Signals 在模型发生变化时告诉框架,使 Zone.js 成为可选的。
- 提供计算属性,而不会在每个变化检测周期中从新计算。
- 实现了更好的与 RxJS 的互操作性。
1.1AngularSignals
AngularSignals 库容许你定义 Reactive 值并表白它们之间的依赖关系。你能够在相应的 RFC 中理解更多对于库的个性。上面是一个如何将其与 Angular 一起应用的简略示例:
@Component({
selector: 'my-app',
standalone: true,
template: `
{{fullName() }} <button (click)="setName('John')">Click</button>
`,
})
export class App {firstName = signal('Jane');
lastName = signal('Doe');
fullName = computed(() => `${this.firstName()} ${this.lastName()}`);
constructor() {effect(() => console.log('Name changed:', this.firstName()));
}
setName(newName: string) {this.firstName.set(newName);
}
}
如上代码段创立了一个计算属性值 fullName, 它依赖 firstName 和 lastNamesignals,咱们也申明了一个 effect, 它的回调函数将会在其读取的信号值每次更新时执行,也就是 firstName 更改时从新执行,以上的 fullName 计算属性意味着它会依赖 firstName 和 lastName 信号值的变动。当咱们设置 firstName 为 ”John” 时,浏览器会打印如下的日志:
"Namechanged:JohnDoe"
1.2RxJS 互操作性
你将可能通过 @angular/core/rxjs-interop 中的函数轻松地将 signals 转换为 observables,该函数作为 v16 开发预览版中的一部分。例如,上面是如何转换 signal 为 observable 的示例:
import {toObservable, signal} from '@angular/core/rxjs-interop';
@Component({...})
export class App {count = signal(0);
count$ = toObservable(this.count);
ngOnInit() {this.count$.subscribe(() => ...);
}
}
上面是一个如何将 observable 的转换为 signal 以防止应用 async 管道的示例:
import {toSignal} from '@angular/core/rxjs-interop';
@Component({
template: `
<li *ngFor="let row of data()"> {{ row}} </li>
`
})
export class App {dataService = inject(DataService);
data = toSignal(this.dataService.data$, []);
}
Angular 用户通常心愿在相干 Subject 实现时实现一个流,以下模式十分常见:
destroyed$ = new ReplaySubject<void>(1);
data$ = http.get('...').pipe(takeUntil(this.destroyed$));
ngOnDestroy() {this.destroyed$.next();
}
接下来,咱们介绍一种新的 RxJS 操作符 takeUntilDestroyed,简略应用示例如下:
data$=http.get('…').pipe(takeUntilDestroyed());
默认状况下,此操作符将注入以后的清理上下文。如果在组件中应用,它将应用组件的生命周期。当你想要将 Observable 的生命周期与特定组件的生命周期分割起来时,takeUntilDestroy 特地有用。而 takeUntilDestroy 是基于下文的 DestroyRef 实现的。
1.3signals 下一阶段
接下来咱们将钻研基于信号组件,信号组件将会简化生命周期钩子函数以及一种简略的申明式输出 (inputs) 和输入(outputs),咱们还将编写一套更残缺的示例和文档。
Angular 仓储中最受欢迎的 Issue 是“Proposal:InputasObservable”。几个月前,咱们回应说要反对这个个性为框架的一部分,咱们很快乐与大家分享,往年晚些时候,咱们将推出一项性能,该性能将启用基于信号的输出——你将可能通过 interop 包将输出转换为可观测值。
二、服务器端渲染和 hydration 加强
依据 Angular 的年度开发者考察,服务器端渲染是 Angular 的第一大改良方向。在过来的几个月里,Angular 与 ChromeAurora 团队单干,改善了 hydration 和服务器端渲染的性能和 DX。明天,Angular 带来了残缺利用非破坏性 hydration 的开发者预览。
在新的残缺利用非破坏性 hydration 中,Angular 不再从头开始从新渲染利用。相同,该框架在构建外部数据结构时查找现有的 DOM 节点,并将事件监听器附加到这些节点上。这么做的益处是:
- 对终端用户来说,页面上没有内容的闪动。
- 在某些状况下有更好的 Web Core Vitals。
- 面向未来的架构,能够用咱们往年晚些时候推出的基元实现细粒度的代码加载。
- 只需几行代码就能与现有的应用程序轻松集成。
- 对于执行手动 DOM 操作的组件,在模板中应用 ngSkipHydration 属性逐渐采纳 hydration。
在晚期测试中,咱们看到 Largest Contentful Paint 通过全应用程序 Hydration 作用进步了 45%。一些利用曾经在生产中实现了 Hydration,并报告了 CWV 的改良:开始体验只须要在 main.ts 中增加如下几行代码即可。
import {
bootstrapApplication,
provideClientHydration,
} from '@angular/platform-browser';
...
bootstrapApplication(RootCmp, {providers: [provideClientHydration()]
});
2.1 新的服务器端渲染个性
作为 v16 版本的一部分,咱们还更新了 Angular Universal 的 ng-add 原理图,使你可能应用独立 API 将服务器端渲染增加到我的项目中。咱们还为内联款式引入了对更严格的 内容安全策略的反对。
2.2 Hydration 和服务端渲染的下一步
v16 中的工作只是一块垫脚石,咱们打算在这里做更多的工作。在某些状况下,有机会提早加载对页面不重要的 JavaScript,并在当前对相干组件进行 Hydrate。这种技术被称为局部 Hydrate,咱们将在下一步对其进行摸索。
自从 Qwik 从谷歌的关闭源代码框架 Wiz 中推广了可恢复性的想法以来,咱们在 Angular 中收到了许多对于这一性能的申请。可恢复性必定在咱们的路线图上,咱们正在与 Wiz 团队密切合作,摸索更多的空间。咱们对它所带来的开发人员体验的限度持审慎态度,评估不同的衡量,并将在咱们获得停顿时随时向你通报。
你能够通过浏览“What’s next for server-side rendering in Angular”查看更多将来的打算。
三、改良对独立组件 / 指令 / 管道的工具
Angular 是一个被数百万开发人员用于许多要害使命的应用程序框架,咱们认真对待重大变更,咱们 几年前 就开始摸索独立的 APIs,2022 年咱们在开发者预览下公布了它们,当初,通过一年多的收集反馈和对 APIs 的迭代,咱们心愿被更宽泛的采纳!
为了反对开发人员将其利用程序转换为独立 APIs,咱们开发了迁徙原理图和独立组件迁徙指南,你进入我的项目执行如下命令:
ng generate @angular/core:standalone
原理图将转换你的代码,删除不必要的 NgModules 类,最初将我的项目的疏导程序更改为应用独立的 APIs。
3.1 独立 ng new 集
作为 Angular v16 的一部分,你能够一开始就创立一个新的独立我的项目,要尝试独立 APIs 原理图的开发预览版,请确保你在 Angular CLI v16 上并运行:
ng new --standalone
你将在没有任何 NgModules 的状况下取得更简略的我的项目目录,此外,我的项目中的所有生成器都将生成独立的指令、组件和管道。
3.2 配置 Zone.js
在独立 APIs 首次公布后,咱们从开发人员那里据说,心愿可能应用新的 bootstrapApplication API 来配置 Zone.js。咱们通过 provideZoneChangeDetection 增加了一些选项:
bootstrapApplication(App, {providers: [provideZoneChangeDetection({ eventCoalescing: true})]
});
3.3 基于 esbuild 的构建零碎的开发预览版
一年多前,咱们发表正在 Angular CLI 中对 esbuild 进行实验性反对,以放慢构建速度。明天,咱们很快乐与大家分享,在 v16 中,咱们基于 esbuild 的构建零碎进入了开发预览版!晚期测试显示,冷生产环境构建改善了 72% 以上。
在 ng serve 中,咱们当初应用 Vite 作为开发服务器,esbuild 提供在开发和生产环境的构建。
咱们想强调的是,Angular CLI 齐全依赖 Vite 作为开发服务器。为了反对选择器匹配,Angular 编译器须要保护组件之间的依赖关系图,这须要与 Vite 不同的编译模型。你能够通过更新 angular.json 来尝试 Vite + esbuild :
...
"architect": {
"build": { /* Add the esbuild suffix */
"builder": "@angular-devkit/build-angular:browser-esbuild",
...
接下来,在咱们将这一个性从开发者预览晋升到正式版之前,咱们将解决对 i18n 的反对问题。
3.4 主动实现模板中的导入
你应用模板中的组件或管道从 CLI 或语言服务中取得谬误的次数是多少次,而实际上没有导入相应的实现?我猜应该是很屡次。语言服务当初容许主动导入组件和管道。
如上动图显示了 VSCode 中 Angular 语言服务的主动导入性能。
四、改善开发者体验
除了咱们重点关注的大型计划外,咱们还致力于引入备受要求的性能。
4.1 输出必填(Required inputs)
自从咱们在 2016 年引入 Angular 以来,如果不为特定输出指定值,就不可能呈现编译时谬误。因为 Angular 编译器在构建时执行查看,因而此更改在运行时减少了零开销,多年来,开发人员始终在要求这个性能,咱们失去了一个强有力的批示,这将十分不便!在 v16 中,能够依据须要标记输出为 required:
@Component(...)
export class App {@Input({ required: true}) title: string = '';
}
4.2 将路由器数据作为组件输出进行传递
路由的开发教训始终在疾速倒退,GitHub 上一个 风行的性能申请 是要求可能将路由参数绑定到相应组件的输出。咱们很快乐与大家分享这一性能现已作为 v16 版本的一部分提供。当初,能够将以下数据传递给路由组件的输出:
- 路由 data — resolvers 和 data 属性
- Path 参数
- Query 参数
以下是如何拜访路由 resolver 数据的示例:
const routes = [
{
path: 'about',
loadComponent: import('./about'),
resolve: {contact: () => getContact()}
}
];
@Component(...)
export class About {
// The value of "contact" is passed to the contact input
@Input() contact?: string;}
4.3 CSP 对内联款式的反对
Angular 在组件款式的 DOM 中蕴含的内联款式元素违反了默认 style-src 内容安全策略(CSP)。要解决此问题,它们应该蕴含一个 nonce 属性,或者服务器应该在 CSP 头中蕴含款式内容的哈希。只管在谷歌,咱们没有发现针对该破绽的有意义的攻打向量,但许多公司施行了严格的 CSP,导致 Angular 仓储上的 性能申请 广受欢迎。
在 Angular v16 中,咱们实现了一个逾越框架、Universal、CDK、Material 和 CLI 的新性能,该性能容许你为 Angular 内联的组件的款式指定 nonce 属性。有两种办法能够指定 nonce:应用 ngCspNonce 属性或通过 CSP_NONCE 注入令牌。
如果您有权拜访服务器端模板,则 ngCspNonce 属性十分有用,该模板能够在结构响应时将 nonce 增加到标头和 index.html 中。
<html>
<body>
<app ngCspNonce="{% nonce %}"></app>
</body>
</html>
另一种办法是通过 CSP_NONCE 注入令牌指定 nonce。如果你在运行时能够拜访 nonce,并且心愿可能缓存 index.html,请应用此办法:
import {bootstrapApplication, CSP_NONCE} from '@angular/core';
import {AppComponent} from './app/app.component';
bootstrapApplication(AppComponent, {
providers: [{
provide: CSP_NONCE,
useValue: globalThis.myRandomNonceValue
}]
});
4.4 灵便的 ngOnDestroy
Angular 的 Lifecycle Hooks 提供了大量的性能,能够插入应用程序执行的不同时刻,如何实现更高的灵活性是一种机会和抉择,例如,提供对 OnDestroy as an observable 拜访作为一种可察看的形式。
在 v16 中,咱们使 OnDestroy 能够被注入,此性能实现了开发人员始终要求的灵活性。新性能容许你注入与组件、指令、服务或管道绝对应的 DestroyRef,并注册 onDestroy 生命周期钩子函数。DestroyRef 能够被注入到注入上下文中的任何地位,包含组件之外 —— 在这种状况下,当相应的注入器被销毁时,ngDestroy 钩子就会被执行:
import {Injectable, DestroyRef} from '@angular/core';
@Injectable(...)
export class AppService {destroyRef = inject(DestroyRef);
destroy() {this.destroyRef.onDestroy(() => /* cleanup */ );
}
}
4.5 主动敞开标签 (Self-closing tags)
咱们最近实现的一个 备受要求的性能,容许你对 Angular 模板中的组件应用自闭标签,这是一个小的开发体验改良,能够为你节俭一些打字工夫。比方:
<super-duper-long-component-name [prop]="someVar"></super-duper-long-component-name>
// 替换为
<super-duper-long-component-name [prop]="someVar" />
4.6 更好、更灵便的组件
在过来的几个季度里,咱们与谷歌的 Material Design 团队密切合作,为 Angular Material 的 Web 提供了 Material 3 实现。咱们在 2022 年交付的基于 MDC Web 的组件为这项工作奠定了根底。
作为下一步,咱们正在致力在往年晚些时候推出一个基于 expressive token-based 的主题化 API,该 API 反对 Angular Material 组件的更高定制。
揭示一下,咱们将在 v17 中删除遗留的、非基于 MDC 的组件,请确保你依照咱们的 迁徙指南 进行迁徙,以取得最新版本。
参考文档:
https://blog.angular.io/angular-v16-is-here-4d7a28ec680d