Angular小博客

分享让你更聪明

[译2021]在语言服务的幕后

浏览:1次 日期:2024年10月20日 19:38:28 作者:admin

语言服务状况

Angular 语言服务创建于 2016 年,当时编辑器体验生态系统(VS Code + TypeScript)仍处于起步阶段。为了向 Angular 用户提供丰富的语言功能,Angular 团队必须克服相当多的底层架构挑战。 View Engine 编译器必须适应语言服务用例,这在构思编译器时并不是设计目标。从那时起,我们在为语言服务带来更多功能和提高稳定性方面取得了巨大进步。然而,存在一些基本问题,例如增量编译性能和更健壮的类型检查。如果不彻底修改编译器,这些是不可能解决的。我们很早就意识到了这一点,并设计了一个路线图,为与 Ivy 编译器集成的全新编辑器体验铺平道路。

如何开始

您可以通过以下方式选择加入 Ivy Language Service 测试版:

基于编译器的架构

Ivy 支持的 Angular 语言服务具有更好的性能,因为它是从头开始构建的增量构建。它完全重写了之前利用 Ivy 编译器的基于 View Engine 的语言服务。

新语言服务设计的核心是与 Angular 编译器的深度集成,Angular 编译器是许多现代语言服务中使用的架构模式。我们的编译器已经对 Angular 代码有了透彻的了解,并且我们已经能够利用它的功能。在某些情况下,我们能够扩展它们——提供更快、更准确、更强大的语言服务体验。

增量构建性能

您是否注意到,在进行更改后运行ng build需要一段时间,但ngserve能够更快地重建应用程序?这是由于编译器支持增量编译。当您第一次运行ngserve时,编译器会读取您的代码并收集有关每个组件及其模板的信息。一旦完成应用程序的构建,编译器不会关闭并忘记所有这些信息 – 它会保持加载状态并等待,直到对应用程序进行更改。发生这种情况时,编译器会查看更改并确定哪些组件可能受到影响。它能够快速决定哪些组件需要重新编译以及哪些组件可以重用之前的工作。

Angular 语言服务将增量编译推向了极限。虽然ngserve会在您每次保存时编译代码,但语言服务通常会在您键入时一遍又一遍地重新编译。作为将编译器集成到语言服务的一部分,我们能够利用 Ivy 增量编译的改进来提供更快、更流畅的 IDE 体验。

投资 Angular 语言服务的编译器优化通常也可以提高编译性能。例如,语言服务主要关注 Angular 模板内部发生的更改。这些更改有一个独特的属性:更改组件的模板不会影响任何其他组件。为了确保 IDE 中的最佳性能,我们利用此属性来最大限度地减少编译器在响应仅模板更改时必须执行的工作量。此优化的影响不仅限于 Angular 语言服务。例如,在仅模板更改后重新编译时, ngserve也会变得更快。

共享模板引擎

Angular 编译器最严峻的挑战之一是执行 Angular 模板的类型检查。 TypeScript 具有复杂且不断改进的类型系统,为模板复制该功能将是一项艰巨的工作。相反,编译器做了一些聪明的事情:在幕后,它为每个模板生成 TypeScript 代码,然后要求 TypeScript 检查代码是否有错误。生成的代码称为“类型检查块”或 TCB。 TypeScript 在 TCB 中发现的错误表明模板中存在真正的问题。

考虑一个UserComponent的模板,它显示用户的姓名和地址:

<p>Name: {{user.name}}</p>
<address-view [address]=”user.address”></address-view>

该组件的简化 TCB 可能如下所示:

function UserComponent_TCB(ctx: UserComponent) {
    ‘’ + ctx.user.name;
    let c0: AddressView = (null!);
    c0.address = ctx.user.address;
}

TCB 的每个部分代表模板中存在的一个绑定或表达式:

function UserComponent_TCB(ctx: UserComponent) {
    ‘’ + ctx.user.name; // {{user.name}}
    let c0: AddressView = (null!); // <address-view> component
    c0.address = ctx.user.address; // [address]=”user.address”
}

在重新设计语言服务时,我们意识到我们不仅可以利用这些 TCB 进行类型检查,还可以实现语言服务的许多功能。例如,假设您正在输入[地址]绑定并希望语言服务为您自动完成。你可能会写:

<address-view [address]=”user.”></address-view>

语言服务应该能够为您自动完成此处的用户对象的属性。为此,我们将不完整的表达式转换为 TCB:

c0.address = ctx.user.;

通过要求 TypeScript 在 TCB 中自动完成此表达式,语言服务可以为模板本身内的表达式提供准确的结果。

高级功能

以这种方式利用编译器为一些长期要求的语言服务功能铺平了道路。我们一直致力于实现与以前的语言服务引擎的功能对等,但 11.1 版本包含一个很棒的新功能:查找引用。

通过利用编译器中的 TCB 引擎,我们现在可以跨 TypeScript 和模板搜索引用。在模板内,“查找引用”可以定位表达式中任何值的声明或其他用法。这在模板和应用程序的 TypeScript 代码中都有效。查找引用也可以在另一个方向上工作:在 TypeScript 代码中搜索符号的引用时,将显示模板引用。

我们对我们的新方法所创造的可能性感到兴奋。我们相信,Angular 编译器在 Angular 语言服务中的利用集成将在未来带来更多新功能。

11.1 中的实验版本

Angular 11.1 发布了 Ivy 语言服务的测试版。它为 Angular 开发人员提供了更高的性能和更准确的诊断。它还进行了一些改进,例如支持通用管道、结构指令和带有复合选择器的指令。

如上所述,该版本为语言服务添加了一项主要功能——查找参考文献。这是新的 ivy 语言服务的第一个主要功能,后续还将推出更多功能。

想要了解 Angular 语言服务的实际应用吗?查看此视频以了解演练:这个是视频

接下来的工作

我们将继续改进和完善 Angular 语言服务。我们希望创造尽可能最好的开发人员体验。今天,Ivy 语言服务可供选择,将来它将成为新项目的默认语言服务。

发现错误?在GitHub上提交问题并提及Ivy 语言服务

原文链接:https://blog.angular.dev/under-the-hood-of-the-language-service-ab763c26f522