Angular 10带来了哪些颠覆性变化?
全文共8096字,预计学习时长21分钟
图源:unsplash
Angular 10正在接近终点线。
就像许多Angular社区的成员一样,笔者也一直关注这个新闻,看看这个框架的下一个主要版本会带来什么。笔者读了很多文章,但仍觉得自己在编程领域还是有很多不明白的,多年的Angular经验并没有让我很轻松地理解那些文章所谈论的内容。
所以,笔者决定写一篇文章介绍Angular 10的新特性,让它更容易理解。
术语
Microsyntax
Angular的microsyntax允许你用一个简洁、友好的字符串来配置指令,microsyntax解析器将该字符串转换为<ng-template>上的属性。因此,不需要<ng-templatengFor[ngForOf]="items"><div>{{item}}</div></ng-template,你应该写<div *ngFor="letitem of items">{{item}}</div>。
绑定表达式
在Angular中有四种不同类型的数据绑定:
· 事件绑定,例如:
· 双向数据绑定:数据在组件和视图之间双向流动的机制。组件和视图总是同步的,在任何一端所做的更改都会立即以两种方式更新。例:
· 插值:表示组件中的变量的文本放在模板中的双花括号之间。例:
· 属性绑定:属性绑定是一种单向机制,允许你设置视图元素的属性。例:
抽象语法树(AST)
图源:unsplash
AST是抽象语法树的缩写,它是指编程语言中由语句和表达式生成的记号。使用AST,解释器或编译器可以生成机器码或计算指令。Angular模板AST是经过转换和注释的HTML AST版本,功能如下:
· 收集引用(# attribute)和变量(let- attributes)
· 在绑定表达式AST中使用收集的变量和引用来解析和转换绑定表达式
· 将Angular模板语法的快捷方式,比如*ngFor和[name]转换成规范版本(和bind-name)
terser
terser是一个针对ES6+的JavaScript解析器、mangler和压缩器工具包。由于不再维护uglify-es,且uglify-js不支持ES6+,Angular CLI团队在Angular CLI 7.0中在构建的缩小阶段使用了terser。
“UrlMatcher”
UrlMatcher是一个用于根据URL匹配路由的函数。当path和pathMatch匹配的组合没有足够的表达能力时,可以为Route.matcher实现自定义URL匹配器。
以下matcher匹配HTML文件:
Bazel
Bazel是Angular CLI的构建器,该CLI在version 10中被弃用。弃用理由如下:
· Bazel的网络生态系统仍在快速发展。
· Angular Ivy编译器的引入,让我们可以以更快、更高效的方式使用Bazel。
· 如果没有对许多Angular用户来说无法接受的折衷,要实现与基于webpack的Angular CLI的功能同等是很难的。
Angular LanguageService
Angular Language Service是一个集成到代码编辑器中的分析引擎,它提供了一种获取完整、跟踪引用、错误、提示和导航的方法。
它可以在单独的HTML文件中使用外部模板,也可以使用内联模板。编辑器会自动检测到你正在打开一个Angular文件,然后它使用Angular语言服务来读取tsconfig.json文件,查找应用程序中的所有模板,然后为打开的任何模板提供语言服务。
图源:unsplash
它可以作为Visual Studio代码、WebStorm和Sublime文本的扩展。你可以使用以下命令安装它:
@angular/language-service是TypeScript语言服务的包装器,它通过对Angular概念的特定理解扩展了TypeScript的分析,可以在Typescript的组件类和模板中的表达式之间架起桥梁。
编译器的哲学
Angular提供了两种方法来编译你的应用:
· 即时编译(JIT),它在运行时在浏览器中编译你的应用程序。这是Angular 8之前的默认设置。
· 提前编译 (AOT),它在构建时编译应用程序和库。自Angular 9以来,这一直是默认方法。
Ivy可以实现编译器(和运行库)的完整重写,以便:
· 实现更合适的构建时间(通过更增量的编译)
· 达到更合理的构建大小(生成的代码与tree shaking更兼容)
· 解锁新的潜在特性(元编程或高阶组件,延迟加载组件而不是模块,一个新的不基于Zone.js的变化检测系统等)
视图引擎是Ivy的前身。可以把在Ivy上完成的工作分为三类,具体如下:
· @
angular/compiler-cli:TypeScript转换器管道,包括两个命令行工具,ngtsc和ngcc
· @angular/compiler:将decorator转换成Ivy的编译器
· @angular/核心:可以由@angular/compiler转换的装饰器
为了理解编译器的设置原理,理解Typescript的底层架构是很有帮助的。
图源:unsplash
TypeScripttranspiler架构
下图显示了正常的tsc流程以及将.ts文件转换为.js文件的步骤,.tsc来自Typescript CLI,它编译由tsconfig.json定义的最近的项目。
TypeScript architecture
下面是这个流程的简要解释:
· Parse:一个传统的递归下降解析器,稍微做了一些调整以支持增量解析,它会发出一个AST,帮助识别当前文件中导入了哪些文件。
· 类型检查器:这一步构建一个符号表,然后对文件中的每个表达式进行类型分析,报告发现的错误。
· 声明降低到ES5,将async方法转换为状态机等等。
· 打印:TS转换为JS。
Angular编译步骤
Angular编译器构建在常规TypeScript架构之上,使用它的三个主要阶段:程序创建、类型检查和发射——加上一个分析步骤和一个解析步骤:
Angularcompiler 架构
· 程序创建:从tsconfig.json开始。
· 分析:一个类一个类地浏览TS代码,寻找Angular的东西,收集关于结构的信息。
· 解决:一次查看整个程序,理解它并做出优化决策。
· 类型检查:就像普通的TypeScript编译器一样,它可以验证组件模板中的表达式并报告错误,这个过程不会被ngtsc扩展或修改。
· emit:为每个修饰类生成Angular代码并对其进行补丁。它将TypeScript代码转换为JavaScript。
两个新的编译器入口点
Angular引入了两个编译器入口点:ngtsc和ngcc。
Ngtsc
ngtsc编译ivy兼容的代码。这是TypeScript到JavaScript编译器,用于查找Angular 装饰器 @component替代品与特定的Angular运行指令/对应指令,像ɵɵdefineComponent。它是tsc. ngc的最小包装,当
angularCompilerOptionenableIvy标志在tsconfig中设置为true时,ngc作为ngtsc运行tsconfig.json文件。
编译流
当ngtsc开始运行时,它首先解析tsconfig.json,然后创建一个ts.Program。在运行上述转换之前,需完成以下几点:
· 必须为包含decorator的输入源文件收集元数据。
· 必须async解决@Component decorator中列出的资源文件。例如,CLI可能希望运行webpack来生成对@Component的styleUrls属性的.css输入。
· 必须运行诊断,这将创建TypeChecker并触及程序中的每个节点(一个相当昂贵的操作)。
Ngcc
ngcc代表Angular兼容性编译器,它将前ivy模块(旧版本)转换为ivy兼容的代码。它可以处理来自npm的代码,并生成与用ngtsc编译的代码相同的Ivy版本。ngcc也可以作为代码加载器(如webpack)的一部分运行,可以将来自node_modules的软件包进行动态置换。
如何使用ngcc
通过添加一个postinstall npm脚本,即可在每次安装node_modules后运行ngcc:
在postinstall中使用ngcc的方法
如果你正在使用Angular 8(这是使用Ivy的第一步),并且想启动一个Ivy驱动的新项目,你可以在ng new命令中使用——enable-ivy标志:
新项目会自动为Ivy配置,enableIvy选项在项目的tsconfig.app.json文件中设置true:
最后,可以在新建的项目文件夹中执行ngc命令来运行编译器:
然后,可以在dist/out-tsc文件夹中检查生成的代码。
Angular 10的主要特性
谷歌开发的升级版框架更多地关注于生态系统的改善而非新功能的改进,Angular 10被设置为比以前的版本更小。到目前为止,最新初步版本的主要功能包括:
图源:unsplash
特性
· language-service:添加了一个封装实际ngtsc编译器的编译器接口。特定于language-service的编译器使用项目接口管理多个typecheck文件,并根据需要创建ScriptInfo。
· language-service:删除HTML实体的自动完成功能。自动完成功能正在从HTML实体中移除,比如&和<等。因为它不是Angular LS的核心功能,其价值和性能成本都有待考虑。
· 编译器:为属性读取和方法调用添加了名称范围。
· 编译器:元数据中加入了依赖项信息和ng-content选择器。此处提及的编译器特性将为工具提供额外的元数据,如Angular语言服务,提供为库中定义的指令/组件提供建议的能力。
· 编译器:在一个微语法表达式到ParsedProperty的ExpressionBinding中传播正确的值,该表达式反过来将范围传播到模板ast (VE和Ivy)。
· ngcc:允许配置异步锁定超时。这增加了对ngcc.config.js文件的支持,用于设置AsyncLocker的retryAttempts和retryDelay选项。集成测试添加一个新的超时检查,并使用ngcc.config.js减少超时时间,以防止测试花费太长时间。
· ngcc:实现基于程序的entry-point finder。这个查找器被设计为只处理tsconfig定义的程序可以访问的tsconfig.json文件。如果安装了大量依赖项,但实际上只有一小部分入口点被导入到应用程序中,那么使用此选项可以对其加速处理。
· bazel:从闭包到开发模式文件的显式映射,该特性针对的是那些必须将生产构建输入转换为开发模式对等物的开发工具。
· 本地化:支持合并多个翻译文件。以前每个地区只能加载一个翻译文件,现在用户可以为每个语言环境指定多个文件,每个文件的翻译将通过一个消息ID合并在一起。所以如果你有三个文件要合并——"a.xlf”、“b.xmb”、 "c.json",则应是来自"a.xlf”的任何消息,而不是来自"b.xmb"或"c.json"的消息。
在实践中,这意味着你应该将文件按重要性排序,最重要的放在最前面,然后再进行回退翻译。
· Router:对于Router,CanLoad guard现在可以返回Urltree了。CanLoad guard返回Urltree取消当前导航并重定向,这与当前可以激活CanActivate保护的行为相匹配,这不会影响预加载。CanLoad保护装置阻止任何预加载;任何带有CanLoad守卫的路由都不会被预加载,守卫也不会作为预加载的一部分被执行。
性能改进
图源:unsplash
· compiler-cli:增量地执行模板类型检查,并将Ivy模板类型检查拆分到多个文件中。
· ngcc:现在允许立即报告过期的锁文件。另外,在运行之间缓存解析后的.tsconfig文件。
· 减少入口点清单文件的大小。包和入口点的基本路径是已知的,因此不需要将它们存储在文件中。避免不必要地存储空数组。以前,即使一个入口点不需要处理,ngcc也会解析入口点的文件来计算依赖关系,这对于大型节点模块来说会花费很多时间。
· ngcc:为了提高性能,basePaths的计算被延迟,所以只有在需要的时候才在TargetedEntryPointFinder中完成工作。以前,只要查找器被实例化,就会计算basePaths,在目标入口点已经被处理的情况下,这无疑是一种浪费。
重要改动
TypeScript 3.9现在是特色,TypeScript 3.8的支持已经被删除。TypeScript 3.6和TypeScript 3.7也不再被支持。
· Service workers:以前,当从缓存中检索资源时,需要考虑Vary标头,这阻止了对缓存资产的检索(serviceworker实现细节),并且由于不同浏览器中不一致的/有bug的实现导致了不可预测的行为。
现在,当从ServiceWorker缓存中检索资源时,会忽略Vary标头,这可能导致即使在它们的标头不同的情况下也会检索到资源。如果你的应用需要根据请求头来区分它的响应,请确保AngularServiceWorker的配置避免缓存受影响的资源。
· Router:任何返回EMPTY都会取消导航。如果想让导航继续,就需要更新解析器来释放一些值,如defaultIfEmpty(…),of(..)等。
· common:关于格式化跨天的日期周期逻辑已经更新。当使用b或B格式代码格式化时间时,所呈现的字符串不能正确处理跨天的日周期,逻辑回到了AM的默认情况。
这个逻辑已经被更新,它将匹配扩展到午夜之后的一天周期内的时间,因此它现在将呈现正确的输出,例如在English的情况下是at night。使用formatDate()或DatePipe或b和B格式代码的应用程序将受到此更改的影响。
· 核心:关于未知元素的警告现在被记录为错误。这不会破坏你的应用程序,但可能会让那些不希望通过console.error记录任何内容的工具出错。
· 核心: Angular npm包不再包含JSDoc注释,以支持闭包编译器的高级优化。在Angular包中对闭包编译器的支持一直处于试验阶段,而且已经有一段时间了。
在TS3.9中,闭包不能用于JavaScript发出。如果你过去使用过Angular的Closure编译器,那么你最好直接使用从源代码构建的Angular包,而不是在npm上使用版本,npm主要是为webpack/Rollup+terser构建管道优化的。
作为临时解决方案,你可以考虑使用带有闭包标志的当前构建管道——compilation_level=SIMPLE。此标志将确保构建管道生成可构建和可运行的构件,但由于禁用了高级优化,负载大小将增加。
· 核心:对ModuleWithProviders来说,通用是强制性的。ModuleWithProviders模式需要一个泛型类型参数来处理Ivy编译和呈现管道,但是在提交之前,视图引擎允许省略泛型类型。如果开发人员在你的应用程序代码中使用没有泛型类型的ModuleWithProviders,版本10迁移将为你更新代码。
然而,如果一个开发人员正在使用视图引擎,并且根据省略泛型类型的库,你现在会得到一个类似于以下错误的构建时错误:
error TS2314: Generic type'ModuleWithProviders<T>' requires 1 type argument(s).
在这种情况下,ngcc不会帮助你(因为它只支持Ivy),而且迁移只涵盖应用程序代码。你应该联系库作者来修复,以便在他们使用这个类时提供一个类型参数。还有一种解决方案,你可以在.tsconfig文件中将skiplibcheck设置为false,或者可以将应用程序更新为使用Ivy。
图源:unsplash
错误修复
· Router:UrlMatcher现在允许返回null。
· 核心
:undecorated-classes-with-decorated-fields迁移不装饰派生类。
· Service workers:防止SW注册策略影响应用稳定。
· 修复了一些错误,包括编译器避免在一个空数组中使用未定义的表达式,以及内核避免在导入不存在的符号时出现迁移错误,在核心中还有一个解决terser inlining bug的方法。
Compiler-Compatibility问题
在不破坏更改的情况下,需要一个兼容性功能来逐步迁移到Ivy,它确保Ivy和非Ivy库在迁移期间可以共存。
不是所有的Angular代码都是同时编译的。应用程序依赖于共享库,这些库以编译后的形式发布在npm上,而不是作为TypeScript源代码。即使一个应用程序是用ngtsc构建的,它的依赖关系也可能不是。
如果一个特定的库不是用ngtsc编译的,那么它的.js中就没有具体化的装饰器属性,如果将其链接到一个没有以相同方式编译的依赖项会在运行时失败。
pre-Ivy转换代码
Ivy代码只能与其他Ivy代码链接,因此需要构建应用程序,npm中的所有前Ivy依赖项都必须转换为Ivy依赖项。这个转换必须作为在应用程序上运行ngtsc的前兆,并且未来的编译和链接操作需要针对这个转换版本的依赖项进行。
图源:unsplash
你可以在GitHub上找到Angular 10的初步版本:
mailto:https://github.com/angular/angular/releases?ref=morioh.com
在the changelog中可以找到更多细节:
https://github.com/angular/angular/blob/master/CHANGELOG.md#1000-next6-2020-05-07
留言点赞关注
我们一起分享AI学习与发展的干货
如转载,请后台留言,遵守转载规范