Angular 6和Ivy渲染引擎

图片 同事们下午好。 我们正在考虑是否要更新Jacob Fine和Anton Moiseev所著的“ Angular and TypeScript。专业人士的网站建设 ”。 今年秋天将发布新版本 ,其中包含有关Angular 5和Angular 6的资料。

最初,我们考虑发布有关Ivy引擎的资料,这可能是Angular 6中最有趣的创新,但是后来我们停止了Cedric Exbright的更多概述出版物(原始版本于5月发布)。

在Angular 6中,有很多严肃的创新,而且其中最重要的创新是您无法命名的功能:这是新的渲染引擎Ivy。 由于该引擎仍处于试验阶段,因此我们将在本文结尾处讨论它,并从其他新功能和革命性变化入手。

摇摇欲坠的提供者

现在,有一种新的建议方法,即使用新的providerIn属性在@Injectable()装饰器中直接注册提供程序。 它以'root'作为应用程序中任何模块的值。 使用'root'实现的对象将作为一个单独的对象在应用程序中注册,并且您无需将其添加到root模块的提供程序中。 同样,当使用providedIn: UsersModule实现的对象将注册为UsersModule提供程序,并且不会添加到模块提供程序中。

 @Injectable({ providedIn: 'root' }) export class UserService { } 

引入了这种新方法以更好地删除应用程序中的非功能代码(摇树)。 当前,情况是这样的:即使未在应用程序中使用服务,添加到模块提供者的服务也将最终确定为最终集合-允许这样做有点可悲。 如果您使用延迟加载,则可能会立即陷入多个陷阱,或者发现自己陷入了错误输入服务的情况。

应用程序中的这种情况不太可能经常发生(如果您编写服务然后使用它),但是第三方模块有时会提供我们不需要的服务-最终,我们有很多无用的JavaScript。

因此,此功能对库开发人员特别有用,但是现在建议以这种方式注册已实现的对象-这也适用于应用程序开发人员。 现在,在使用服务时,默认情况下,新的CLI甚至还使用了providedIn: 'root'支架。

同样,您现在可以声明一个InjectionToken ,直接在InjectionToken中注册它providedIn并在此处添加一个factory

 export const baseUrl = new InjectionToken<string>('baseUrl', { providedIn: 'root', factory: () => 'http://localhost:8080/' }); 

请注意:这也简化了单元测试。 为了进行此类测试,它们用于向测试模块的提供者注册服务。 这是我们之前所做的:

 beforeEach(() => TestBed.configureTestingModule({ providers: [UserService] })); 

现在,如果UserService使用providedIn: 'root'

 beforeEach(() => TestBed.configureTestingModule({})); 

不必担心:仅在确实需要时,所有在providerIn中注册的providedIn都不会加载到测试中,而是被延迟实例化。

Rxjs 6

Angular 6现在在内部使用RxJS 6,因此您需要牢记这一点来更新应用程序。

并且... RxJS 6正在改变导入方法!

在RxJS 5中,您可以编写:

 import { Observable } from 'rxjs/Observable'; import 'rxjs/add/observable/of'; import 'rxjs/add/operator/map'; const squares$: Observable<number> = Observable.of(1, 2) .map(n => n * n); 

在RxJS 5.5中,出现了可传递语句:

 import { Observable } from 'rxjs/Observable'; import { of } from 'rxjs/observable/of'; import { map } from 'rxjs/operators'; const squares$: Observable<number> = of(1, 2).pipe( map(n => n * n) ); 

在RxJS 6.0中,导入已更改:

 import { Observable, of } from 'rxjs'; import { map } from 'rxjs/operators'; const squares$: Observable<number> = of(1, 2).pipe( map(n => n * n) ); 

因此,有一天您将不得不在整个应用程序中更改导入。 我写“一次”,而不是“现在”,因为RxJS兼容的库是在RxJS中发布的,它允许将RxJS下载到6.0版,即使旧版本仍在整个应用程序或所用的一个库中使用语法。

Angular团队已就此主题编写了整个文档 ,在迁移到Angular 6.0之前绝对有必要阅读它。

请注意:这是一个非常酷的tslint规则集,称为rxjs-tslint 。 其中只有4条规则,如果将它们添加到项目中,系统将自动迁移所有导入和RxJS代码,这是通过最简单的tslint --fix来完成的! 毕竟,如果您仍然不知道,在tslint有一个fix选项可以自动修复它发现的所有错误! 它甚至更容易使用:全局安装rxjs-tslint并运行rxjs-5-to-6-migrate -p src/tsconfig.app.json 。 我在我们的一个项目中尝试了rxjs-tslint ,并且效果很好(至少运行两次以折叠所有导入)。 查看该项目的自述文件以了解更多详细信息: github.com/ReactiveX/rxjs-tslint

如果您有兴趣了解有关RxJS 6.0的更多信息,建议您参考Ben Lesch关于ng-conf的下一份报告

i18n

与i18n相关的最重要的前景是能够“在运行时生成i18n”,而不必为每个本地点分别构建应用程序。 该功能尚不可用(只有原型),并且需要Ivy引擎才能运行(更多信息请参见下文)。

与i18n相关的另一项更改已经进行并且可以使用。 货币渠道已以最有效的方式进行了优化:现在,它不像以前那样将所有货币舍入到两位数,而是舍入到所需的位数(例如,对于巴林第纳尔,则舍入为3,对于智利比索,则舍入为0)。

如果需要,可以使用新的i18n getNumberOfCurrencyDigits函数以编程方式检索此值。

其他方便的格式化功能(例如formatDateformatCurrencyformatPercentformatNumber也出现在formatPercent formatNumber

足够方便的是,如果您要应用与通道中相同的转换,但是可以从TypeScript代码中执行。

动画制作

在Angular 6.0中,除非使用AnimationBuilder ,否则无需polyfill web-animations-js就可以实现AnimationBuilder 。 您的应用程序可以赢得一些宝贵的字节! 如果浏览器不支持element.animate API,则Angular 6.0会回滚到CSS关键帧的使用。

角度元素

Angular Elements是一个项目,允许您将Angular组件包装为Web组件并将其嵌入到不使用Angular的应用程序中。 最初,该项目仅存在于“ Angular Lab”中(也就是说,它仍处于试验阶段)。 在v6中,它排在最前列,并正式包含在框架中。 这是一个重要的话题,值得单独撰写。

ElementRef <T>

如果要在模板中使用元素链接,则可以使用@ViewChild@ViewChildren ,甚至可以直接实现ElementRef 。 这种情况下的缺点是:在Angular 5.0或更低版本中,指定的ElementRef将为nativeElement属性获得any类型。

在Angular 6.0中,可以根据需要输入更严格的ElementRef:

 @ViewChild('loginInput') loginInput: ElementRef<HTMLInputElement>; ngAfterViewInit() { // nativeElement  `HTMLInputElement` this.loginInput.nativeElement.focus(); } 

什么被认为是不受欢迎的,什么正在根本上改变

让我们谈谈进行迁移时需要记住的事项!

preserveWhitespaces :默认为false

在“升级过程中可能发生的问题”部分中,我们注意到默认情况下,preserveWhitespaces现在为false 。 此选项出现在Angular 4.4中,如果您想同时期待什么-这是该主题的完整文章 。 剧透:一切都可以做,或者可以完全破坏您的模板。

ngModel和反应形式

以前,可以使用ngModelformControl提供相同的表单字段,但是今天,这种做法被认为是不希望的,并且在Angular 7.0中不再受支持。

这里有点混乱,整个机制起作用了,也许不是您所期望的( ngModel这是您不久之前熟悉的指令,但是formControl指令的输入/输出执行几乎相同但不相同的任务)。

所以现在,如果我们应用代码:

 <input [(ngModel)]="user.name" [formControl]="nameCtrl"> 

然后我们会收到警告。

您可以将应用程序配置为显示alwaysonce ), once (一次)或never (永远)的警告。 默认值always

 imports: [ ReactiveFormsModule.withConfig({ warnOnNgModelWithFormControl: 'never' }); ] 

为过渡到Angular 7做准备的一种或另一种方式,您需要修改代码以使用面向模板的形式或反应形式。

Ivy项目:Angular中的新(新)渲染引擎

...... 这是Angular的第4个主要版本(2、4、5、6),并且第三次重写了渲染引擎!

切记:Angular将您的模板编译为等效的TypeScript代码。 然后,使用您在JavaScript中编写的TypeScript编译此TypeScript,结果供用户使用。 并且在我们之前已经是Angular中该渲染引擎的第三个版本(第一个版本是Angular 2.0的初始版本,第二个版本是Angular 4.0)。

在此新版本的渲染引擎中,编写模板的方法没有改变,但是,它优化了许多指标,尤其是:

  • 建立时间
  • 表盘尺寸

所有这一切仍在进行深入试验,新的Ivy渲染引擎通过复选框打开,如果要尝试,必须将其放入编译器选项(在tsconfig.json文件中)。

 "angularCompilerOptions": { "enableIvy": true } 

请注意,此机制可能不太可靠,因此请不要在生产中使用它。 也许他仍然没有工作。 但是在不久的将来它将被接受为默认选项,因此您应该尝试一次,看看它是否在您的应用程序中起作用,以及从中受益。

让我们更详细地讨论Ivy与旧的渲染引擎的区别。

旧引擎生成的代码

让我们PonyComponent一个小例子:让我们有一个PonyComponent组件,该组件采用PonyModel模型(带有namecolor参数)并显示小马的图像(取决于衣服)以及小马名称。

看起来像这样:

 @Component({ selector: 'ns-pony', template: `<div> <ns-image [src]="getPonyImageUrl()"></ns-image> <div></div> </div>` }) export class PonyComponent { @Input() ponyModel: PonyModel; getPonyImageUrl() { return `images/${this.ponyModel.color}.png`; } } 

Angular 4中引入的渲染引擎为每个模板生成了一个名为ngfactory的类。 该类通常包含(简化的代码):

 export function View_PonyComponent_0() { return viewDef(0, [ elementDef(0, 0, null, null, 4, "div"), elementDef(1, 0, null, null, 1, "ns-image", View_ImageComponent_0), directiveDef(2, 49152, null, 0, i2.ImageComponent, { src: [0, "src"] }), elementDef(3, 0, null, null, 1, "div"), elementDef(4, null, ["", ""]) ], function (check, view) { var component = view.component; var currVal_0 = component.getPonyImageUrl(); check(view, 2, 0, currVal_0); }, function (check, view) { var component = view.component; var currVal_1 = component.ponyModel.name; check(view, 4, 0, currVal_1); }); } 

很难阅读,但是此代码的主要部分描述如下:

  • 创建的DOM的结构,其中包含元素的定义( figureimgfigcaption ),它们的属性和文本节点的定义。 视图定义数组中的DOM结构的每个元素都由其自己的索引表示。
  • 变化检测功能; 它们中包含的代码将检查模板中使用的表达式是否得出与以前相同的值。 在此, getPonyImageUrl方法的结果,如果更改了结果,则将更新图像组件的输入值。 小马昵称也是如此:如果更改,包含此昵称的文本节点将被更新。

常春藤生成的代码

如果我们使用Angular 6,并且将enableIvy标志设置为true ,则在同一示例中将不会生成单独的ngfactory 。 信息将直接嵌入到组件本身的静态字段中(简化代码):

 export class PonyComponent { static ngComponentDef = defineComponent({ type: PonyComponent, selector: [['ns-pony']], factory: () => new PonyComponent(), template: (renderFlag, component) { if (renderFlag & RenderFlags.Create) { elementStart(0, 'figure'); elementStart(1, 'ns-image'); elementEnd(); elementStart(2, 'div'); text(3); elementEnd(); elementEnd(); } if (renderFlag & RenderFlags.Update) { property(1, 'src', component.getPonyImageUrl()); text(3, interpolate('', component.ponyModel.name, '')); } }, inputs: { ponyModel: 'ponyModel' }, directives: () => [ImageComponent]; }); // ...   } 

现在,所有内容都包含在此静态字段中。 template属性包含与熟悉的ngfactory等效的属性,但结构略有不同。 与以前一样, template功能将在进行任何更改时启动,但现在有两种模式:

  • 创建模式:正在创建组件,它包含需要创建的静态DOM节点
  • 每次更改都会执行其余功能(如有必要,将更新图像源和文本节点)。

这有什么变化?

现在,所有装饰器都直接内置到其类中( @Injectable @Pipe@Directive@Pipe @Directive ),要生成它们,您只需要了解当前的装饰器即可。 Angular团队将此现象称为“局部性原则”:重新编译组件,您无需重新分析应用程序。

生成的代码略有减少,但是更重要的是,可以消除许多依赖关系,从而在应用程序的其中一部分发生更改时加快了重新编译的速度。 此外,对于现代的收集器(例如Webpack),一切都变得更加漂亮:非功能性代码被安全地切断,即框架中那些您不需要的部分。 例如,如果您在应用程序中没有通道,那么它们的解释所需的框架甚至不会包含在最终集中。

我们习惯于使Angular代码繁重。 有时并不可怕,但是经过压缩和压缩后,Hello World重达37 kb。 当Ivy负责生成代码时,非功能代码将被更有效地切断。 现在,压缩后的Hello World压缩为7.3 kb,压缩后仅为2.7 kb,这是一个很大的差异。 压缩后的TodoMVC应用程序-仅12.2 kb。 这是来自Angular团队的数据,其他人无法与我们合作,因为要使Ivy能够按此处所述的方式工作,您仍然需要手动对其进行修补。

有关更多详细信息,请使用ng-conf进行本次演讲

与现有库的兼容性

您可能会感兴趣:如果在项目中使用Ivy,已经以旧格式发布的库会发生什么? 不用担心:即使没有Ivy进行编译,引擎也会为项目的依赖项提供Ivy兼容版本。 我现在不会公开内部,但是所有细节都必须透明。

新功能

让我们考虑一下使用此显示引擎时我们将拥有哪些新机会。

模板中的私有属性

新引擎增加了新功能或潜在的变化。
这种情况直接与以下事实有关:模板函数嵌入在组件的静态字段中:现在,我们可以在模板中使用组件的私有属性。 以前这是不可能的,因此我们不得不公开模板中使用的组件的所有字段和方法,并且将它们ngfactory为单独的类( ngfactory )。 从另一个类访问私有属性时,TypeScript编译将失败。 现在已经过去了:由于模板函数位于静态字段中,因此可以访问组件的私有属性。

我看到了Angular团队成员的评论,尽管现在可以这样做,但不建议在模板中使用私有属性,因为将来可能再次禁止使用……因此,明智的做法是继续仅在模板中使用公共字段! 无论如何,编写单元测试现在变得更加容易,因为测试可以检查组件的状态,而无需为此生成和检查DOM。

i18n在运行时

请注意:新的渲染引擎最终为我们带来了期待已久的机会,并提供了“运行时i18n”。 在撰写本文时,她还没有准备好,但是我们一次看到了几次提交,这是一个好兆头!
很棒的事情是,如果您已经在使用i18n,则不必更改应用程序。 但是现在,您不需要为计划支持的每个语言环境重建应用程序-只需上载带有每个语言环境翻译的JSON,其余的工作就由Angular来完成!

AoT库

当前,在NPM中发布的库必须发布metadata.json文件,并且不能发布其组件的AoT代码。 这是可悲的,因为与这种组装相关的成本被转移到了我们的应用程序中。 使用Ivy,不需要元数据文件,并且库作者现在可以将其AoT代码直接发布到NPM!

改进的堆栈轨道

现在,如果您的模板有问题,那么生成的代码应该提供改进的堆栈跟踪-导致一个整洁的错误,指示发生模板的行。 您甚至可以在模板中设置断点,并跟踪Angular中实际发生的情况。

NgModule消失吗?

这仍然是遥遥无期的前景,但也许将来没有NgModules也可以实现。 这种变化的第一个迹象是可摇树的提供者,并且逻辑上假设Ivy对于准备逐渐放弃NgModule(或至少使其响应速度较慢)的人具有所有必要的基本块。 没错,所有这一切仍在将来,我们会耐心等待。

此版本中不会有很多新功能,但是Ivy对于未来肯定是有趣的。 尝试一下-我想知道您会如何喜欢它!

Source: https://habr.com/ru/post/zh-CN419389/


All Articles