
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 AnbieterJetzt 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 6Angular 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.
i18nDie 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.
AnimationenIn 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.
WinkelelementeAngular 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() {
Was als unerwünscht erkannt wird und was sich grundlegend ändertLassen 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 FormenFrü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 AngularSoooo ... 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:
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 CodePonyComponent
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 generiertWenn 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 BibliothekenSie 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 FunktionenLassen Sie uns überlegen, welche neuen Möglichkeiten wir bei der Arbeit mit dieser Display-Engine haben werden.
Private Eigenschaften in VorlagenEine 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 LaufzeitBitte 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-BibliothekenDerzeit 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 StapelspurenJetzt 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!