datenbindung und performance in angular 2
TRANSCRIPT
20.10.2016
1
ManfredSteyer
Über mich …
• Manfred Steyer
• SOFTWAREarchitekt.at
• Trainer & Consultant
• GDE & MVP
• Focus: Angular 2
Page 3
ManfredSteyer
20.10.2016
2
Ziele
• Wie funktioniert die Datenbindung in Angular 2?
• Wie kann man die Performance für Datenbindung verbessern?• Immutables
• Observables
Nicht-Ziele
• Allgemeine Einführung in Angular 2
20.10.2016
3
Didaktik
• 1. Abschnitt• Präsentation
• Live-Coding
• 2. Abschnitt• Übung mit Übungsblatt und Starterkit
Inhalt• Überblick zur Datenbindung in Angular 2
• Datenbindung hinter den Kulissen
• Two-Way-Binding
• Überblick zu Immutables
• Performancetuning mit Immutables
• Überblick zu Observables
• Performancetuning mit Observables
• Übung
• Bonus: Ausblick auf AOT-Kompilierung
20.10.2016
4
Benchmark
Überblick zur Datenbindung in Angular 2
20.10.2016
5
Angular-2-Anwendungen sind Komponenten
Page 12
@Component({selector: 'flight-search',templateUrl: 'flight-search.html'
})export class FlightSearchComponent {
from: string;to: string;flights: Array<Flight>;
constructor(http: Http) { }
search() { [...] }select(flight: Flight) { [...] }
}
Page 13
<input [(ngModel)]="from"><input [(ngModel)]="to">
<button [disabled]="!from || !to" (click)="search()">Search
</button>
<table><tr *ngFor="let f of flights">
<td>{{f.id}}</td><td>{{f.date}}</td><td>{{f.from}}</td><td>{{f.to}}</td>
</tr></table>
Template
20.10.2016
6
Hinter den Kulissen
Page 14
Architektur-Ziele von Angular 2
Page 15
Performance Komponenten Vorhersagbarkeit
20.10.2016
7
Data-Binding in AngularJS 1.x
Page 16
Model Model Directive
Komponentne-Baum in Angular 2
Page 17
Komponente für gesamte App
Komponente (z. B. list)
Komponente
(z. B. list-item)
Komponente
(z. B. list-item)
20.10.2016
8
Regeln für Property Bindings
• Komponente hängt nur von Daten des Parents ab
• Komponente hängt nie von Daten der Kinder ab
• Abhängigkeits-Graph is ein Baum
• Angular benötigt nur eine einzige Iteration („digest“) zum Abgleich des Baumes
Page 18
Property-Binding
Page 19
model
item item
{{ item.title }} {{ item.title }}
[http://victorsavkin.com/post/110170125256/change-detection-in-angular-2]
20.10.2016
9
Event-Bindings (One-Way, Bottom/Up)
Page 20
{{ item.title }} {{ item.title }}
Event-Handler
Event-Handler
Event-Bindings (One-Way, Bottom/Up)
• Kein Digest nötig
• Event-Handler == Callbacks
• Aber: Events können Anwendungszustand verändern Digest um Property-Bindings zu aktualisieren
Page 21
20.10.2016
10
Property- und Event-Bindings
Page 22
Property-Bindings
ausführen
Event-Handler warden
ausgeführt
Ereignis tritt ein
Anwendung ist bereit!
Alle Handler ausgeführt
Properties gebunden
Bindings definieren
Page 23
20.10.2016
11
View
Page 24
<button (click)="search()" [disabled]="!from || !to">Search</button>
<table>
<tr *ngFor="let flight of flights">
<td>{{flight.id}}</td>
<td>{{flight.date}}</td>
<td>{{flight.from}}</td>
<td>{{flight.to}}</td>
<td><a href="#" (click)="selectFlight(flight)">Select</a></td>
</tr>
</table>
<td [text-content]="flight.id"></td>
Recap
• Property-Binding: One-Way; Top/Down
• Event-Binding: One-Way; Bottom/Up
• Two-Way-Binding?
• Two-Way = Property-Binding + Event-Binding
Page 25
20.10.2016
12
Property- und Event-Bindings kombinieren
Page 26
<input [ngModel]="from" (ngModelChange)="updateFrom($event)">
updateFrom(newValue) {
this.from = newValue;
}
Property- und Event-Bindings kombinieren
Page 27
<input [ngModel]="from" (ngModelChange)="from = $event">
20.10.2016
13
Syntax-Zucker für Two-Way-Binding
Page 28
<input [(ngModel)]="from">
Vorteile
• Performance
• Events: Flexibilität
• Syntaxzucker: Einfache Nutzung
Page 29
20.10.2016
14
DEMO: Two-Way-Binding
Page 30
Beispiel: flight-card
Page 31
20.10.2016
15
Einsatz der flight-card
Page 32
<div *ngFor="let f of flights">
<flight-card [item]="f"
[selectedItem]="selectedFlight"
(selectedItemChange)="selectedFlight = $event">
</flight-card>
</div>
flight-card
Page 33
flug-carditem
selectedItem
>
> > selectedItemChange
flug
selectedFlug
20.10.2016
16
Beispiel: flight-card
@Component({selector: 'flight-card',templateUrl: './flight-card.component.html'
})export class FlightCard {
@Input() item;@Input() selectedItem;@Output() selectedItemChange = new EventEmitter();
select() {this.selectedItemChange.emit(this.item);
}}
Template<div style="padding:20px;"
[ngStyle]="{'background-color': (selectedItem == item) ? 'orange' : 'lightsteelblue' }" >
<h2>{{item.from}} - {{item.to}}</h2><p>Flugnr. #{{item.id}}</p><p>Datum: {{item.date}}</p>
<p><button class="btn btn-default"
(click)="select()">Select
</button></p>
</div>
20.10.2016
17
DEMO
Performance-Tuning mit Immutables
20.10.2016
18
Angular traversiert standardmäßig den gesamten Komponenten-Baum
flights
flight flight
{{ flight.id }} {{ flight.id }}
FlightSearch
Card Card
Immutables
• Unveränderbare Objekte
• Wenn sich repräsentierte Daten ändern: Neues Objekt erzeugen
• Man kann einfach herausfinden, ob sich etwas geändert hat• oldObject == newObject
• Mit oder ohne Bibliotheken möglich (wie immutable.js)
20.10.2016
19
Beispiel
const ONE_MINUTE = 1000 * 60;
let oldFlights = this.flights;
let oldFlight = oldFlights[0]; // Flight to change!
let oldFlightDate = new Date(oldFlight.date); // Date to change
Änderung ohne Immutables
date.setTime(date.getTime() + ONE_MINUTE * 15);
this.flights[0].date = date.toISOString();
20.10.2016
20
Änderung mit Immutables
let newFlightDate = new Date(oldFlightDate.getTime() + ONE_MINUTE * 15);
let newFlight = {
id: oldFlight.id,
from: oldFlight.from,
to: oldFlight.to,
date: newFlightDate.toISOString()
};
let newFlights = [
newFlight,
...oldFlights.slice(1, this.flights.length)
];
this.flights = newFlights;
Auf Änderungen prüfen
console.debug("Array: " + (oldFlights == newFlights)); // false
console.debug("#0: " + (oldFlights[0] == newFlights[0])); // false
console.debug("#1: " + (oldFlights[1] == newFlights[1])); // true
20.10.2016
21
Immutables und Angular 2
• Annahme: Jedes @Input() einer Komponente ist immutable
• Angular kann einfach prüfen, ob sich eingehende Daten einer Komponente ändern
• Nur wenn das der Fall ist, müssen Komponente und deren Kind-Komponenten betrachtetet werden
Immutables und Angular
flights
flight flight
{{ flight.id }} {{ flight.id }}
FlightSearch
Card Card
Änderung
20.10.2016
22
Optimierung aktivieren
@Component({[…]changeDetection: ChangeDetectionStrategy.OnPush
})export class FlightCard {
[…]@Input flight;
}
DEMO
20.10.2016
23
Weitere Vorteile von Immutables
• Vereinfacht Nachvollziehbarkeit und Testing -> Kein Zustand
• Vereinfacht Undo/Redo
Observables
20.10.2016
24
Was sind Observables?
• Repräsentieren asynchrone Daten, die im Verlauf der Zeit veröffentlicht werden
Observer„Senke“
Observable„Quelle“
Operator(z. B. map)
20.10.2016
25
Observable
Observable
.subscribe((result) => { … },(error) => { … },() => { … }
);
Observer
Beispiel
this.http.get("http://www.angular.at/api/...").map(resp => resp.json()).subscribe(
(flights) => { … },(err) => { console.error(err); }
);
20.10.2016
26
Eigenes Observable
var observable = Observable.create((sender) => {
sender.next(4711);sender.next(815);
//sender.error("err!");
sender.complete();
});
Asynchron, Ereignis-gesteuert
var subscription = observable.subscribe(…);
subscription.unsubscribe();
return () => { console.debug('Bye bye'); };
Cold vs. Hot Observables
Page 55
Cold
• Standard
• Punkt zu Punkt
• Pro Empfänger ein Sender
• Sender startet erst bei
Anmeldung
Hot
• Punkt zu Multipunkt
• Sender startet auch ohne
Anmeldungen
20.10.2016
27
Hot Observable
Page 56
var o = Observable.create((observer) => {
observer.next(42);observer.error("err!");
}).publish().connect();
Subjects
Subject
.subscribe((result) => { … },(error) => { … },() => { … }
);
Observer
Daten/ Benachrichtigungvon außen
20.10.2016
28
Subjects
Page 58
SubjectHot & verteilt
empfangene Daten
BehaviorSubjectSpeichert letzten
Wert
ReplaySubjectSpeichert mehrere
Werte
Performanceoptimierung mit Observables
20.10.2016
29
Performance und Observables
• Observable benachrichtigt über Datenänderungen
• Datenbindung wird bei Benachrichtigung aktiv
• Aktivierung: OnPush
Observables mit OnPush
Page 61
flights
flight$ flight$
{{ flight$.id }} {{ flight$.id }}
FlightSearch
Card Card
Änderung
20.10.2016
30
Observables mit OnPush
Page 62
flights$
flight flight
{{ flight.id }} {{ flight.id }}
FlightSearch
Card
Änderung
Card
Observable binden
<flight-card
[item]="flight | async" […]>
</flight-card>
20.10.2016
31
DEMO
Nicht „Alles-oder-Nichts“
• Optimierungen mit Immutables und Observables funktionieren nicht nach dem „Alles-oder-Nichts“-Prinzip
• Sie können bei Bedarf genutzt werden
20.10.2016
32
DEMO
Fazit
• Property-Bindings: Top/Down, One-Way
• Event-Bindings: Bottom/Up, One-Way
• 2-Way-Bindings: Property-Binding + Event-Binding
• Architektur: „Fast by default“
• Wer mehr Performance benötigt: Immutables und Observables
20.10.2016
33
Kontakt
[mail] [email protected]
[web] SOFTWAREarchitekt.at
[twitter] ManfredSteyer
Bonus: AOT-Kompilierung
20.10.2016
34
Zusätzliche Performance
HTML
TemplateJavaScript
Template Compiler
input['value'] = flight['date'] input.value = flight.date;
Ansätze für das Kompilieren von Templates
• JIT: Just im Time, zur Laufzeit
• AOT: Ahead of Time, beim Build
20.10.2016
35
Vorteile von AOT
• Bessere Startup-Performance
• Kleinere Bundles: Compiler für JIT muss nicht ausgeliefert werden
• Bessere statische Analysierbarkeit des Programmcodes
HTML
TypeScript
TypeScript
Angular 2
Tree Shaking
20.10.2016
36
Installation des AOT-Compilers (ngc)
• npm install @angular/compiler-cli --save-dev
• npm install [email protected] --save-dev
• npm install @angular/platform-server --save
Page 78
tsconfig.aot.json
• module: ES2015
• Target: ES2015
Page 79
"angularCompilerOptions": {
"genDir": "aot",
"skipMetadataEmit" : true
}
20.10.2016
37
webpack2
• UglifyJsPlugin
• LoaderOptionsPlugin
• --optimize-minimize
Page 80
Kompilieren
• ngc -p tsconfig.aot.json
Page 81
20.10.2016
38
Bootstrapping
Page 82
import { platformBrowser } from '@angular/platform-browser';import { AppModuleNgFactory } from './app.module.ngfactory';
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);
DEMO
20.10.2016
39
Fazit
• Hohes Potential (Performance, Bundle-Größe, Tree-Shaking)
• Sehr junges Feature
• Tooling entsteht gerade (z. B. Integration in WebPack)
• Wird wohl erst ab Version 2.1 „runde Sache“
Kontakt
[mail] [email protected]
[web] SOFTWAREarchitekt.at
[twitter] ManfredSteyer