Angular 6 und Ivy Rendering Engine

Published on August 06, 2018

Angular 6 und Ivy Rendering Engine

Ursprünglicher Autor: Cédric Exbrayat
  • Übersetzung
BildGuten Tag, Kollegen. Wir überlegen, ob das Buch " Angular and TypeScript. Website Building for Professionals " von Jacob Fine und Anton Moiseyev aktualisiert werden soll . Die neue Ausgabe erscheint in diesem Herbst und enthält Material zu Angular 5 und 6.

Zuerst dachten wir, Material zur Ivy-Engine zu veröffentlichen, die wahrscheinlich die interessanteste Innovation in Angular 6 sein wird, aber dann stoppten wir bei einer Veröffentlichung von Cedric Exbraat (das Original wurde im Mai veröffentlicht) ).

In Angular 6 gibt es einige gravierende Neuerungen, von denen die wichtigste nicht als Feature bezeichnet werden kann: Dies ist Ivy, die 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.

Vom Baum verwackelbare Anbieter

Jetzt gibt es eine neue, empfohlene Möglichkeit, einen Anbieter direkt im Dekorateur @Injectable()mit einem neuen Attribut zu registrieren providedIn. Es nimmt 'root'den Wert jedes Moduls Ihrer Anwendung an. Wenn Sie ein 'root'injiziertes Objekt verwenden, wird es in der Anwendung als Einzelgänger registriert, und Sie müssen es nicht zu den Anbietern im Stammmodul hinzufügen. In ähnlicher Weise wird das providedIn: UsersModulezu injizierende Objekt , wenn es angewendet wird, als Anbieter registriert UsersModuleund nicht zu den Modulanbietern hinzugefügt.

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

Diese neue Methode wurde eingeführt, um nichtfunktionalen Code in der Anwendung besser zu entfernen (Tree-Shaking). Derzeit ist die Situation so, dass der Dienst, der den Anbietern des Moduls hinzugefügt wird, im endgültigen Satz enthalten ist, auch wenn er nicht in der Anwendung verwendet wird - und es ist ein wenig traurig, dies zuzugeben. Wenn Sie eine faule Ladung verwenden, können Sie in mehrere Fallen gleichzeitig geraten oder sich in einer Situation befinden, in der der Dienst im falschen Satz aufgeführt wird.

Es ist unwahrscheinlich, dass eine solche Situation in Anwendungen häufig auftritt (wenn Sie einen Dienst schreiben, verwenden Sie ihn). In Modulen von Drittanbietern bieten wir jedoch manchmal Dienste an, die wir nicht benötigen. Infolgedessen verfügen wir über eine ganze Reihe von nutzlosem JavaScript.

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

Ebenso können Sie es jetzt deklarieren InjectionToken, direkt bei registrieren providedInund hier hinzufügen factory:

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

Hinweis: Dies vereinfacht auch das Testen von Einheiten. Zu diesem Zweck sind wir es gewohnt, den Dienst bei den Anbietern von Testmodulen zu registrieren. So haben wir es zuvor gemacht:

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

Wenn der UserService nun Folgendes verwendet providedIn: 'root':

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

Machen Sie sich keine Sorgen: Alle mit registrierten Dienste providedInwerden nicht in den Test geladen, sondern nur dann instanziiert, wenn sie wirklich benötigt werden.

RxJS 6

Angular 6 verwendet jetzt intern RxJS 6, sodass Sie die Anwendung aktualisieren müssen, um dies widerzuspiegeln.

Und ... RxJS 6 ändert den Ansatz für den Import!

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

Pipeable Operatoren erschienen in 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)
);

Und 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", nicht "jetzt", weil die RXJS-kompatible Bibliothek in RXJS veröffentlicht wurde, sodass Sie auf RXJS 6.0 aktualisieren können, auch wenn die alten Versionen in Ihrer gesamten Anwendung oder in einer der verwendeten Bibliotheken noch verwendet werden Syntax.

Das Angular-Team hat ein komplettes Dokument zu diesem Thema verfasst, das unbedingt gelesen werden muss, bevor auf Angular 6.0 migriert wird.

Hinweis: Es gibt ein sehr cooles Regelwerk, das hier als tslint bezeichnet wird rxjs-tslint. Es gibt nur 4 Regeln, und wenn Sie sie zum Projekt hinzufügen, migriert das System automatisch alle Ihre Importe und den RxJS-Code, und dies ist die einfachste tslint --fix! Wenn Sie es noch nicht wissen, tslintgibt es eine Optionfix, korrigiert automatisch alle gefundenen Fehler! Es kann noch einfacher verwendet werden: global installieren rxjs-tslintund ausführen rxjs-5-to-6-migrate -p src/tsconfig.app.json. Ich habe rxjs-tslinteines unserer Projekte ausprobiert , und es hat ganz gut funktioniert (führen Sie es mindestens zweimal aus, um auch alle Importe zusammenzufassen). Lesen Sie die README-Datei dieses Projekts, wenn Sie mehr erfahren möchten: github.com/ReactiveX/rxjs-tslint .

Wenn Sie daran interessiert sind, RxJS 6.0 weiter zu studieren, empfehle ich den folgenden Bericht von Ben Lesch über ng-conf.

i18n

Die wichtigste i18n-Perspektive ist die Möglichkeit, „i18n zur Laufzeit“ auszuführen, ohne eine Anwendung für jeden lokalen Punkt separat erstellen zu müssen. Bisher ist diese Funktion nicht verfügbar (es gibt nur Prototypen), und für den Betrieb ist die Ivy-Engine erforderlich (mehr dazu weiter unten).

Ein weiterer i18n-Wechsel hat bereits stattgefunden und ist verfügbar. Der Währungskanal wurde auf effizienteste Weise optimiert: Er rundet nun alle Währungen nicht mehr wie bisher auf bis zu 2 Zeichen, sondern auf die erforderliche Anzahl von Zeichen (z. B. bis zu 3 bei einem bahrainischen Dinar oder bis zu 0 bei einem chilenischen Peso).

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

Im Allgemeinen ist es auch Zugriff auf andere praktische Formatierungsfunktionen, wie zum Beispiel formatDate, formatCurrency, formatPercentund formatNumber.

Es ist sehr praktisch, wenn Sie dieselben Transformationen anwenden möchten, die in den Channels durchgeführt wurden, dies jedoch über TypeScript-Code.

Animationen

In Angular 6.0 sind Animationen bereits ohne Polyfill möglich web-animations-js, sofern Sie sie nicht verwenden AnimationBuilder. Ihre Anwendung kann ein paar wertvolle Bytes gewinnen! Falls der Browser die API nicht unterstützt element.animate, führt Angular 6.0 ein Rollback auf die Verwendung von CSS-Keyframes durch.

Winkelelemente

Angular Elements ist ein Projekt, mit dem Angular-Komponenten in Form von Web-Komponenten verpackt und in eine Anwendung eingebettet werden können, die Angular nicht verwendet. Dieses Projekt existierte zunächst nur im „Angular Laboratory“ (dh es ist noch experimentell). Mit der Veröffentlichung von v6 rückt es ein wenig in den Vordergrund und wird offiziell in das Framework aufgenommen. Dies ist ein großes Thema, das einen eigenen Artikel verdient.

ElementRef <T>

Wenn Sie an der Vorlage einen Link zu dem Element erhalten möchten, können Sie verwenden , @ViewChildoder @ViewChildren, oder sogar direkt zu implementieren ElementRef. Der Nachteil in diesem Fall ist folgender: In Angular 5.0 oder niedriger ElementReferhält der angegebene nativeElementTyp einen Typ für die Eigenschaft any.

In Angular 6.0 können Sie ElementRef genauer 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

Sprechen wir darüber, was Sie bei der Migration beachten müssen!

preserveWhitespaces: Standardfalse

In dem Abschnitt „Probleme, die während des Upgrades auftreten können“ wird darauf hingewiesen, dass preserveWhitespaces jetzt standardmäßig gleich ist false. Diese Option erschien in Angular 4.4, und wenn Sie sich fragen, was Sie zur gleichen Zeit erwarten können, finden Sie hier einen ganzen Beitrag zu diesem Thema. Spoiler: Alles kann ohne auskommen und kann Ihre Vorlagen komplett zerstören.

ngModelund reaktive Formen

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

Hier herrscht ein wenig Verwirrung, und der gesamte Mechanismus hat möglicherweise nicht wie erwartet funktioniert ( ngModel- es war für Sie lange Zeit eine vertraute Direktive und die Eingabe / Ausgabe einer Direktive formControl, die fast dieselbe, aber nicht identische Aufgabe ausführt).

Wenn wir nun den Code anwenden:

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

dann bekommen wir eine Warnung.

Sie können die Anwendung so konfigurieren, dass always(immer), once(einmal) oder never(nie) eine Warnung ausgegeben wird. Die Standardeinstellung ist gültig always.

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

In jedem Fall müssen Sie zur Vorbereitung des Übergangs zu Angular 7 den Code anpassen, um entweder vorlagenorientierte Formulare oder reaktive Formulare zu verwenden.

Project Ivy: eine neue (neue) Rendering-Engine in Angular

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

Denken Sie daran: Angular kompiliert Ihre Vorlagen in äquivalenten TypeScript-Code. Anschließend wird dieses TypeScript zusammen mit dem in JavaScript geschriebenen TypeScript kompiliert und das Ergebnis steht dem Benutzer zur Verfügung. Und wir haben bereits die dritte Version dieser Rendering-Engine in Angular (die erste befand sich in der ursprünglichen 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. Er optimiert jedoch eine Reihe von Indikatoren, insbesondere:

  • Montagezeit
  • Größe einstellen

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

"angularCompilerOptions": {
  "enableIvy": true
}

Bedenken Sie, dass dieser Mechanismus wahrscheinlich nicht sehr zuverlässig ist. Verwenden Sie ihn daher noch nicht in der Produktion. Vielleicht verdient er immer noch nicht. In naher Zukunft wird es jedoch als Standardoption akzeptiert. Probieren Sie es also einmal aus, um festzustellen, ob es in Ihrer Anwendung funktioniert und was Sie davon haben.

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

Der von der alten Engine generierte Code

Betrachten Sie ein kleines Beispiel: Lassen Sie uns eine Komponente haben PonyComponent, die ein Modell als Eingabe PonyModel(mit Parametern nameund color) verwendet und ein Pony-Bild (je nach Anzug) sowie den Namen eines Ponys 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 Rendering-Engine, die in Angular 4 erschien, erzeugte für jede Vorlage eine Klasse mit dem Namen ngfactory. Die normalerweise enthaltene Klasse (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 erstellt DOM, die die Definition der Elemente enthält ( figure, img, figcaption) und die Definition von deren Attributen Textknoten. Jedes Element der DOM-Struktur in einem Array von Ansichtsdefinitionen wird durch einen eigenen Index dargestellt.
  • Erkennungsfunktionen ändern; Der darin enthaltene Code überprüft, ob die in der Vorlage verwendeten Ausdrücke dieselben Werte wie zuvor ergeben. Hier wird das Ergebnis der Methode überprüft getPonyImageUrlund bei Änderungen der Eingabewert für die Bildkomponente aktualisiert. Gleiches gilt für den Namen eines Ponys: Wenn sich dieser ändert, wird der Textknoten mit diesem Namen aktualisiert.

Der Code erzeugt Ivy

Wenn wir mit kantigem 6 arbeiten, und das Flag enableIvygesetzt ist true, wird das gleiche Beispiel nicht getrennt erzeugt werden ngfactory; 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 Attribut templateenthält das Äquivalent des Üblichen ngfactorymit einer etwas anderen Struktur. Die Funktion templatewird nach wie vor bei jeder Änderung gestartet, hat jetzt jedoch zwei Modi:

  • Erstellungsmodus: Die Komponente wird nur 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 Quelle des Bildes und den Textknoten).

Was ändert sich dadurch?

Nun sind alle Dekorateure werden direkt in ihre Klassen eingebettet (das gleiche gilt für @Injectable, @Pipe, @Directive) und für ihre Generation nur über den aktuellen Dekorateur wissen sollten. Dieses Phänomen im Angular-Team wird als "Prinzip der Lokalität" 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. Bei modernen Sammlern, wie zum Beispiel Webpack, fällt zudem alles viel schöner aus: Der nicht funktionierende Code wird zuverlässig abgeschnitten, die Teile des Frameworks, die Sie nicht verwenden. Wenn Sie zum Beispiel keine Kanäle in der Anwendung haben, ist das für deren Interpretation erforderliche Framework nicht einmal im endgültigen Satz enthalten.

Wir sind an den Angular-Code gewöhnt, der schwer wird. Es passiert, es ist nicht beängstigend, aber Hello World wiegt 37 kb nach der Minimierung und Komprimierung ist zu viel. Wenn Ivy für die Codegenerierung verantwortlich ist, wird der nicht funktionierende Code wesentlich effizienter abgeschnitten. Jetzt ist Hello World nach der Minimierung auf 7,3 KB komprimiert, und nach der Komprimierung - nur auf 2,7 KB, was ein großer Unterschied ist. Die TodoMVC-Anwendung ist nach der Komprimierung nur noch 12,2 KB groß. Hierbei handelt es sich um Daten des Angular-Teams, und die anderen konnten nicht mit uns zusammenarbeiten, da Ivy für die hier beschriebene Arbeitsweise noch manuell gepatcht werden muss.

Wenn Sie an Details interessiert sind, schauen Sie sich diesen Auftritt mit ng-conf an.

Kompatibel mit vorhandenen Bibliotheken

Sie könnten interessiert sein: Was passiert mit Bibliotheken, die bereits im alten Format veröffentlicht wurden, wenn Sie Ivy in Ihrem Projekt verwenden? Keine Sorge: Die Engine erstellt eine Ivy-kompatible Version der Abhängigkeiten Ihres Projekts, auch wenn diese ohne Ivy kompiliert wurden. Ich werde Ihnen das Interieur jetzt noch nicht sagen, aber alle Details sollten transparent sein.

Neue Funktionen

Überlegen Sie, welche neuen Funktionen bei der Arbeit mit dieser Display-Engine zur Verfügung stehen.

Private Eigenschaften in Vorlagen

Neue Engine fügt neue Funktionen oder mögliche Änderungen 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 den Vorlagen verwenden. Zuvor war dies nicht möglich. Aus diesem Grund mussten wir alle in der Vorlage verwendeten Felder und Komponentenmethoden öffentlich machen und sie fielen in eine separate Klasse ( ngfactory). Wenn Sie von einer anderen Klasse aus auf eine private Eigenschaft zugreifen, schlägt die TypeScript-Kompilierung fehl. Jetzt ist es Vergangenheit: Da sich die Vorlagenfunktion in einem statischen Feld befindet, wird der Zugriff auf die privaten Eigenschaften der Komponente geöffnet.

In einem Kommentar der Mitglieder des Angular-Teams wurde darauf hingewiesen, dass die Verwendung privater Eigenschaften in Vorlagen nicht empfohlen wird, obwohl dies jetzt möglich ist - da dies in Zukunft möglicherweise wieder verboten ist. Daher ist es wahrscheinlich sinnvoller, 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, auch ohne dieses DOM zu generieren oder zu überprüfen.

i18n während der Ausführung

Bitte beachten Sie: Die neue Rendering-Engine eröffnet uns endlich eine lang ersehnte Gelegenheit und gibt "i18n während der Ausführung". Zum Zeitpunkt dieses Schreibens war es noch nicht ganz fertig, aber wir haben mehrere Commits auf einmal gesehen, und dies ist ein gutes Zeichen!
Das Tolle ist, dass Sie Ihre Anwendung kaum ä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 warten möchten - es reicht aus, nur JSON mit Übersetzungen für jedes Gebietsschema zu laden, und Angular kümmert sich um den Rest!

Bibliotheken mit AoT-Code

Derzeit muss eine von NPM freigegebene Bibliothek die Datei metadata.json veröffentlichen und kann den AoT-Code ihrer Komponenten nicht veröffentlichen. Dies ist bedauerlich, 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 Muster

Jetzt sollte der generierte Code verbesserte Spektren liefern, wenn Sie ein Problem mit Ihren Vorlagen haben - führen Sie zu einem ordentlichen Fehler, der die Vorlagenzeile angibt, in der er aufgetreten ist. Sie können sogar Haltepunkte in Vorlagen festlegen und verfolgen, was in Angular tatsächlich geschieht.

NgModulewird verschwinden?

Dies ist noch in weiter Ferne, aber vielleicht wird es in Zukunft möglich sein, auf NgModules zu verzichten. Die ersten Anzeichen für solche Änderungen sind baumschüttelbare Anbieter, und es ist logisch anzunehmen, dass Ivy über alle erforderlichen Grundbausteine ​​für diejenigen verfügt, die bereit sind, NgModules schrittweise aufzugeben (oder sie zumindest weniger erfolgreich zu machen). Es stimmt, alles ist noch in Sicht, wir werden geduldig sein.

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

Nur registrierte Benutzer können an der Umfrage teilnehmen. Bitte melden Sie sich an.

Die Relevanz des Themas Angular 6