Angular 6 und Ivy Rendering Engine

Bild Guten Tag, Kollegen. Wir überlegen, ob wir das Buch von Jacob Fine und Anton Moiseev " Angular and TypeScript. Website-Erstellung für Profis " aktualisieren sollen. Eine neue Ausgabe erscheint im Herbst und enthält Material zu Angular 5 und 6.

Zuerst dachten wir daran, Material über die Ivy-Engine zu veröffentlichen, die wahrscheinlich die interessanteste Innovation in Angular 6 ist, aber dann stoppten wir bei einer übersichtlicheren Veröffentlichung von Cedric Exbright (das Original wurde im Mai veröffentlicht).

In Angular 6 gab es viele ernsthafte Innovationen, von denen die wichtigsten Funktionen nicht genannt werden können: Dies ist Ivy, eine neue Rendering-Engine. Da der Motor noch experimentell ist, werden wir am Ende dieses Artikels darüber sprechen und mit anderen neuen Funktionen und revolutionären Änderungen beginnen.

Baumschüttelbare Anbieter

Jetzt gibt es eine neue, empfohlene Möglichkeit, den Anbieter mithilfe des neuen Attributs " @Injectable() direkt im @Injectable() zu registrieren. Es nimmt 'root' als Wert eines Moduls in Ihrer Anwendung. Bei Verwendung von 'root' implementierte Objekt in der Anwendung als Einzelgänger registriert, und Sie müssen es nicht zu Anbietern im Root-Modul hinzufügen. In ähnlicher Weise wird bei Verwendung von providedIn: UsersModule implementierte Objekt als UsersModule Anbieter registriert und nicht zu den UsersModule hinzugefügt.

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

Eine solche neue Methode wurde eingeführt, um nicht funktionierenden Code in der Anwendung besser zu entfernen (Baumschütteln). Gegenwärtig ist die Situation so, dass der Dienst, der den Anbietern des Moduls hinzugefügt wird, im endgültigen Satz landet, auch wenn er nicht in der Anwendung verwendet wird - und dies zuzulassen, ist ein wenig traurig. Wenn Sie das verzögerte Laden verwenden, können Sie in mehrere Fallen gleichzeitig geraten oder sich in einer Situation befinden, in der der Dienst im falschen Satz eingegeben wird.

Es ist unwahrscheinlich, dass eine solche Situation in Anwendungen häufig auftritt (wenn Sie einen Dienst schreiben und dann verwenden), aber Module von Drittanbietern bieten manchmal Dienste an, die wir nicht benötigen. Daher verfügen wir über eine ganze Reihe nutzloser JavaScript-Dateien.

Daher ist diese Funktion besonders für Bibliotheksentwickler nützlich. Jetzt wird empfohlen, implementierte Objekte auf diese Weise zu registrieren. Dies gilt auch für Anwendungsentwickler. Die neue CLI verwendet jetzt sogar standardmäßig das providedIn: 'root' Gerüst, wenn Sie mit Diensten arbeiten.

Auf die gleiche Weise können Sie jetzt ein InjectionToken deklarieren, es direkt bei providedIn registrieren und hier eine factory hinzufügen:

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

Bitte beachten Sie: Dies vereinfacht auch das Testen von Einheiten. Für solche Tests werden sie verwendet, um den Dienst bei den Anbietern des Testmoduls zu registrieren. Folgendes haben wir zuvor getan:

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

Wenn der UserService jetzt "In" verwendet providedIn: 'root' :

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

Keine Sorge: Alle bei providedIn registrierten Dienste werden nicht in den Test geladen, sondern nur dann träge instanziiert, wenn sie wirklich benötigt werden.

Rxjs 6

Angular 6 verwendet jetzt intern RxJS 6, daher müssen Sie die Anwendung vor diesem Hintergrund aktualisieren.

Und ... RxJS 6 ändert den Importansatz!

In RxJS 5 könnten Sie schreiben:

 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); 

In RxJS 5.5 wurden pipeable Anweisungen angezeigt:

 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) ); 

In RxJS 6.0 haben sich die Importe geändert:

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

Eines Tages müssen Sie also die Importe innerhalb der gesamten Anwendung ändern. Ich schreibe "einmal" und nicht "gerade jetzt", da die rxJs-kompatible Bibliothek in RxJS veröffentlicht wurde, mit der Sie RxJS auf Version 6.0 herunterladen können, auch wenn die alten Versionen noch in Ihrer gesamten Anwendung oder in einer der verwendeten Bibliotheken verwendet werden Syntax.

Das Angular-Team hat ein vollständiges Dokument zu diesem Thema verfasst. Es ist unbedingt erforderlich, es vor der Migration auf Angular 6.0 zu lesen.

Bitte beachten Sie: Hier ist ein sehr cooler Tslint-Regelsatz namens rxjs-tslint . Es gibt nur 4 Regeln, und wenn Sie sie dem Projekt tslint --fix das System automatisch alle Ihre Importe und RxJS-Code, und dies geschieht mit dem einfachsten tslint --fix ! Wenn Sie es immer noch nicht wissen, gibt es in tslint eine fix Option, die automatisch alle gefundenen Fehler behebt! Es kann noch einfacher verwendet werden: Installieren Sie rxjs-tslint global und führen Sie rxjs-5-to-6-migrate -p src/tsconfig.app.json . Ich habe rxjs-tslint in einem unserer Projekte ausprobiert und es hat ganz gut funktioniert (mindestens zweimal ausführen, um auch alle Importe zu rxjs-tslint ). Weitere Informationen finden Sie in der README-Datei dieses Projekts: github.com/ReactiveX/rxjs-tslint .

Wenn Sie mehr über RxJS 6.0 erfahren möchten, empfehle ich den nächsten Bericht von Ben Lesch über ng-conf.

i18n

Die wichtigste Perspektive im Zusammenhang mit i18n ist die Möglichkeit, "i18n zur Laufzeit" zu erstellen, ohne die Anwendung für jeden lokalen Punkt separat erstellen zu müssen. Diese Funktion ist noch nicht verfügbar (es gibt nur Prototypen), und für den Betrieb wird eine Ivy-Engine benötigt (mehr dazu weiter unten).

Eine weitere Änderung in Bezug auf i18n hat bereits stattgefunden und ist verfügbar. Der Währungskanal wird auf die effizienteste Weise optimiert: Jetzt rundet er alle Währungen nicht wie bisher auf 2 Ziffern, sondern auf die gewünschte Anzahl von Ziffern (z. B. auf 3 beim Bahrain-Dinar oder auf 0 für den chilenischen Peso).

Bei Bedarf kann dieser Wert programmgesteuert mit der neuen Funktion i18n getNumberOfCurrencyDigits abgerufen werden.

Andere praktische Formatierungsfunktionen wie formatCurrency , formatPercent formatNumber und formatNumber ebenfalls formatPercent formatNumber .

Praktischerweise, wenn Sie dieselben Transformationen anwenden möchten, die in den Kanälen ausgeführt werden, dies jedoch über TypeScript-Code tun.

Animationen

In Angular 6.0 sind Animationen bereits ohne Polyfill web-animations-js , es sei denn, Sie verwenden AnimationBuilder . Ihre Anwendung kann einige wertvolle Bytes gewinnen! Falls der Browser die element.animate API nicht unterstützt, wird Angular 6.0 auf CSS-Keyframes zurückgesetzt.

Winkelelemente

Angular Elements ist ein Projekt, mit dem Sie Angular-Komponenten als Webkomponenten umschließen und in eine Anwendung einbetten können, die Angular nicht verwendet. Dieses Projekt existierte zunächst nur im „Angular Lab“ (das heißt, es ist noch experimentell). Mit v6 tritt es ein wenig in den Vordergrund und ist offiziell im Framework enthalten. Dies ist ein großes Thema, das einen separaten Artikel verdient.

ElementRef <T>

Wenn Sie einen @ViewChild in Ihre Vorlage @ViewChild @ViewChildren , können Sie @ViewChild oder @ViewChildren oder sogar ElementRef direkt implementieren. Der Nachteil in diesem Fall ist folgender: In Angular 5.0 oder niedriger erhält das angegebene ElementRef den Typ any für die nativeElement Eigenschaft.

In Angular 6.0 können Sie ElementRef stricter eingeben, wenn Sie möchten:

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

Was als unerwünscht erkannt wird und was sich grundlegend ändert

Lassen Sie uns darüber sprechen, was Sie bei der Migration beachten müssen!

preserveWhitespaces : Standardwert false

Im Abschnitt „Probleme, die während des Upgrades auftreten können“ stellen wir fest, dasserveWhitespaces jetzt standardmäßig false ist. Diese Option wurde in Angular 4.4 angezeigt. Wenn Sie sich fragen, was Sie gleichzeitig erwarten können, finden Sie hier einen vollständigen Beitrag zu diesem Thema. Spoiler: Alles kann oder Ihre Vorlagen vollständig beschädigen.

ngModel und reaktive Formen

Früher war es möglich, dasselbe Formularfeld mit ngModel und formControl , aber heute wird diese Vorgehensweise als unerwünscht angesehen und in Angular 7.0 nicht mehr unterstützt.

Hier tritt ein wenig Verwirrung auf, und der gesamte Mechanismus hat möglicherweise nicht wie erwartet ngModel ( ngModel war eine ngModel vor nicht allzu langer Zeit vertraute Anweisung, aber die Eingabe / Ausgabe der formControl Anweisung, die fast dieselbe, aber nicht identische Aufgabe ausführt).

Wenn wir also den Code anwenden:

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

dann bekommen wir eine Warnung.

Sie können die Anwendung so konfigurieren, dass eine Warnung von always ( once ), once (einmal) oder never (nie) angezeigt wird. Der Standardwert ist always .

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

Um den Übergang zu Angular 7 vorzubereiten, müssen Sie den Code auf die eine oder andere Weise anpassen, um entweder vorlagenorientierte Formulare oder reaktive Formulare zu verwenden.

Ivy-Projekt: Neue (neue) Rendering-Engine in Angular

Soooo ... Dies ist die 4. Hauptversion von Angular (2, 4, 5, 6), und die Rendering-Engine wird zum dritten Mal neu geschrieben!

Denken Sie daran: Angular kompiliert Ihre Vorlagen in äquivalenten TypeScript-Code. Anschließend wird dieses TypeScript mit dem TypeScript kompiliert, das Sie in JavaScript geschrieben haben, und das Ergebnis steht dem Benutzer zur Verfügung. Und vor uns liegt bereits die dritte Version dieser Rendering-Engine in Angular (die erste war in der ersten Version von Angular 2.0 und die zweite in Angular 4.0).

In dieser neuen Version der Rendering-Engine ändert sich der Ansatz zum Schreiben von Vorlagen nicht, optimiert jedoch eine Reihe von Indikatoren, insbesondere:

  • Bauzeit
  • Zifferblattgröße

All dies ist noch sehr experimentell und die neue Ivy-Rendering-Engine wird durch ein Kontrollkästchen tsconfig.json , das Sie in die Compiler-Optionen (in der Datei tsconfig.json ) tsconfig.json , wenn Sie es ausprobieren möchten.

 "angularCompilerOptions": { "enableIvy": true } 

Bitte beachten Sie, dass dieser Mechanismus möglicherweise nicht zu zuverlässig ist. Verwenden Sie ihn daher noch nicht in der Produktion. Vielleicht arbeitet er immer noch nicht. In naher Zukunft wird es jedoch als Standardoption akzeptiert. Sie sollten es daher einmal ausprobieren, um zu prüfen, ob es in Ihrer Anwendung funktioniert und was Sie davon profitieren.

Lassen Sie uns genauer diskutieren, wie sich Ivy von der älteren Rendering-Engine unterscheidet.

Vom alten Motor generierter Code

PonyComponent wir uns ein kleines Beispiel an: Lassen Sie uns eine PonyComponent Komponente haben, die das PonyModel Modell (mit den name und PonyModel ) verwendet und das Bild des Ponys (je nach Anzug) sowie den Pony-Namen anzeigt.

Es sieht so aus:

 @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`; } } 

Die in Angular 4 eingeführte Rendering-Engine hat für jede Vorlage eine Klasse namens ngfactory generiert. Die Klasse enthielt normalerweise (Code vereinfacht):

 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); }); } 

Es ist schwer zu lesen, aber die Hauptteile dieses Codes werden wie folgt beschrieben:

  • Die Struktur des erstellten DOM, die die Definitionen der Elemente ( figure , figure , figcaption ), ihre Attribute und Definitionen der figcaption enthält. Jedes Element der DOM-Struktur im Array der Ansichtsdefinitionen wird durch einen eigenen Index dargestellt.
  • Änderungserkennungsfunktionen; Der darin enthaltene Code prüft, ob die in der Vorlage verwendeten Ausdrücke zu denselben Werten wie zuvor führen. Hier wird das Ergebnis der Methode getPonyImageUrl und bei Änderungen der Eingabewert für die Bildkomponente aktualisiert. Gleiches gilt für den Pony-Spitznamen: Wenn er sich ändert, wird der Textknoten, der diesen Spitznamen enthält, aktualisiert.

Ivy hat Code generiert

Wenn wir mit Angular 6 arbeiten und das enableIvy Flag auf true , wird im selben Beispiel keine separate ngfactory generiert. Informationen werden direkt in das statische Feld der Komponente selbst eingebettet (vereinfachter Code):

 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]; }); // ...   } 

Jetzt ist alles in diesem statischen Feld enthalten. Das template Attribut enthält das Äquivalent der bekannten ngfactory mit einer etwas anderen Struktur. Die template wird nach wie vor bei jeder Änderung gestartet, verfügt nun jedoch über zwei Modi:

  • Erstellungsmodus: Die Komponente wird gerade erstellt und enthält die statischen DOM-Knoten, die erstellt werden müssen
  • Der Rest der Funktion wird bei jeder Änderung ausgeführt (aktualisiert bei Bedarf die Bildquelle und den Textknoten).

Was ändert sich daran?

Jetzt sind alle Dekorateure direkt in ihre Klassen integriert (dasselbe gilt für @Injectable , @Pipe , @Directive ). @Directive zu generieren, müssen Sie nur den aktuellen Dekorateur kennen. Dieses Phänomen wird vom Angular-Team als „Lokalitätsprinzip“ bezeichnet: Um eine Komponente neu zu kompilieren, müssen Sie die Anwendung nicht erneut analysieren.

Der generierte Code wird geringfügig reduziert, aber was noch wichtiger ist, es ist möglich, eine Reihe von Abhängigkeiten zu beseitigen, wodurch die Neukompilierung beschleunigt wird, wenn sich einer der Teile der Anwendung ändert. Darüber hinaus ist bei modernen Sammlern, z. B. Webpack, alles viel schöner: Nicht funktionierender Code wird sicher abgeschnitten, die Teile des Frameworks, die Sie nicht verwenden. Wenn Sie beispielsweise keine Kanäle in der Anwendung haben, ist das für deren Interpretation erforderliche Framework nicht einmal im endgültigen Satz enthalten.

Wir sind es gewohnt, Angular-Code schwer zu machen. Manchmal ist es nicht beängstigend, aber Hello World mit einem Gewicht von 37 KB nach Minimierung und Komprimierung ist zu viel. Wenn Ivy für die Generierung des Codes verantwortlich ist, wird nicht funktionierender Code viel effizienter abgeschnitten. Jetzt wird Hello World nach der Minimierung auf 7,3 KB und nach der Komprimierung komprimiert - nur auf 2,7 KB, und das ist ein großer Unterschied. TodoMVC-Anwendung nach Komprimierung - nur 12,2 kb. Dies sind Daten des Angular-Teams, und andere konnten mit uns nicht arbeiten, da Ivy, um wie hier beschrieben zu funktionieren, immer noch manuell gepatcht werden muss.

Weitere Informationen finden Sie in diesem Vortrag mit ng-conf.

Kompatibilität mit vorhandenen Bibliotheken

Sie könnten interessiert sein an: Was passiert mit Bibliotheken, die bereits im alten Format veröffentlicht wurden, wenn Ivy in Ihrem Projekt verwendet wird? Keine Sorge: Die Engine erstellt eine Ivy-kompatible Version der Abhängigkeiten Ihres Projekts, auch wenn diese ohne Ivy kompiliert wurden. Ich werde das Innere jetzt nicht freigeben, aber alle Details müssen transparent sein.

Neue Funktionen

Lassen Sie uns überlegen, welche neuen Möglichkeiten wir bei der Arbeit mit dieser Display-Engine haben werden.

Private Eigenschaften in Vorlagen

Eine neue Engine fügt eine neue Funktion oder eine mögliche Änderung hinzu.
Diese Situation hängt direkt damit zusammen, dass die Vorlagenfunktion in das statische Feld der Komponente eingebettet ist: Jetzt können wir die privaten Eigenschaften unserer Komponenten in Vorlagen verwenden. Dies war bisher unmöglich, weshalb wir gezwungen waren, alle Felder und Methoden der in der Vorlage verwendeten Komponente öffentlich zu machen, und sie fielen in eine separate Klasse ( ngfactory ). Beim Zugriff auf eine private Eigenschaft von einer anderen Klasse aus schlägt die TypeScript-Kompilierung fehl. Jetzt ist es in der Vergangenheit: Da sich die Vorlagenfunktion in einem statischen Feld befindet, hat sie Zugriff auf die privaten Eigenschaften der Komponente.

Ich habe einen Kommentar von Angular-Teammitgliedern darüber gesehen, dass es nicht empfohlen wird, private Eigenschaften in Vorlagen zu verwenden, obwohl dies jetzt möglich ist - da dies in Zukunft möglicherweise wieder verboten ist. Daher ist es wahrscheinlich klüger, weiterhin nur öffentliche Felder in Vorlagen zu verwenden! In jedem Fall ist das Schreiben von Komponententests jetzt einfacher, da der Test den Status einer Komponente überprüfen kann, ohne das DOM dafür zu generieren und zu überprüfen.

i18n zur Laufzeit

Bitte beachten Sie: Die neue Rendering-Engine eröffnet uns endlich eine lang erwartete Gelegenheit und gibt "i18n zur Laufzeit". Zum Zeitpunkt des Schreibens war sie noch nicht ganz fertig, aber wir haben mehrere Commits gleichzeitig gesehen, und das ist ein gutes Zeichen!
Das Coole ist, dass Sie Ihre Anwendung nicht so ziemlich ändern müssen, wenn Sie bereits mit i18n arbeiten. Jetzt müssen Sie die Anwendung nicht mehr für jedes Gebietsschema neu erstellen, das Sie unterstützen möchten. Laden Sie einfach JSON mit Übersetzungen für jedes Gebietsschema hoch, und Angular kümmert sich um den Rest!

AoT-Bibliotheken

Derzeit muss eine in NPM freigegebene Bibliothek die Datei metadata.json veröffentlichen und kann den AoT-Code ihrer Komponenten nicht veröffentlichen. Dies ist traurig, da die mit einer solchen Montage verbundenen Kosten an unsere Anwendung weitergegeben werden. Mit Ivy ist keine Metadatendatei erforderlich, und Bibliotheksautoren können ihren AoT-Code jetzt direkt in NPM veröffentlichen!

Verbesserte Stapelspuren

Jetzt sollte der generierte Code verbesserte Stapelspuren liefern, wenn Sie ein Problem mit Ihren Vorlagen haben. Dies führt zu einem sauberen Fehler, der die Zeile der Vorlage angibt, in der er aufgetreten ist. Sie können sogar Haltepunkte in den Vorlagen festlegen und verfolgen, was in Angular wirklich passiert.

NgModule verschwinden?

Dies ist noch eine ferne Perspektive, aber vielleicht wird es in Zukunft möglich sein, auf NgModules zu verzichten. Die ersten Anzeichen für solche Änderungen sind baumschüttelnde Anbieter, und es ist logisch anzunehmen, dass Ivy über alle erforderlichen Grundblöcke für diejenigen verfügt, die bereit sind, NgModules schrittweise aufzugeben (oder sie zumindest weniger reaktionsfähig zu machen). Das alles ist zwar noch in der Zukunft, wir werden geduldig sein.

Es wird nicht viele neue Funktionen in dieser Version geben, aber Ivy ist definitiv interessant für die Zukunft. Experimentieren Sie damit - ich frage mich, wie es Ihnen gefallen wird!

Source: https://habr.com/ru/post/de419389/


All Articles