Angular 14.2 bringt Best Practices im Umgang mit Bildern und Standalone-Routing
Das Webframework erweitert die Standalone API und bringt als größte Neuerung eine neue Direktive unter dem Namen NgOptimizedImage.
- Rainer Hahnekamp
Die aktuelle Hauptversion Angular 14 brachte erstmals Standalone Components mit, die den NgModule-Einsatz optional machten. Knapp drei Monate nach dem Release ist Angular 14.2 als zweite Minor-Version erschienen und erweitert die Standalone API um den Router. Als größte Änderung bringt das Release jedoch eine neue Direktive unter dem Namen NgOptimizedImage
mit.
Best Practices für Bilder
Die Optimierung der Bundlesize – das Bundle sind die JavaScript-Datei(en) der Frontendanwendung – ist eine sehr zeitintensive und fordernde Tätigkeit für Entwicklerinnen und Entwickler. Doch je kleiner die Datei, desto schneller lässt sich die Anwendung im Browser starten.
Dabei ist zu beachten, dass ein Browser nicht nur JavaScript-Dateien lädt. Bilder, vor allem bildschirmfüllende, können das Vielfache eines Bundles ausmachen. Das ist vor allem dann sehr schmerzhaft, wenn das besagte Bild nicht einmal im Viewport ist und dadurch beim initialen Laden gar nicht benötigt würde.
Hier kommt die neue Direktive NgOptimizedImage
ins Spiel. Um sie zu aktivieren, ist in <img>
das Attribut src
durch rawSrc
auszutauschen.
<!-- ohne NgOptimizedImage -->
<img [src]="'/assets/' + holiday.imageUrl" [alt]="holiday.title" />
<!-- mit NgOptimizedImage -->
<img [rawSrc]="'/assets/' + holiday.imageUrl" [alt]="holiday.title" />
Listing 1: Aktivierung von NgOptimizedImage
Mit Listing 1 führt die neue Direktive bereits diverse Checks durch. Sollten die Attribute für width
oder height
fehlen, wird das Bild nicht dargestellt, sondern es erscheint eine Fehlermeldung. Das Weglassen der Dimensionsangaben führt nämlich zum sogenannten Content Shifting beziehungsweise Cumulative Layout Shift (CLS) nach Core Web Vitals. Dabei wird das Layout durch das nachträgliche Laden der Bilder verschoben. Das ist keine gute User Experience und lässt sich mit dem Setzen der Attribute sehr einfach vermeiden.
Ferner wird jedes Bild standardmäßig lazy geladen. Das heißt, der Browser weiß, dass die Entwickler das Bild nicht zu den kritischen Elementen zählen. Das kann dazu führen, dass weit außerhalb des Viewports liegende Bilder erst bei Bedarf geladen werden.
Beim verwendeten Beispiel lässt sich dieses Verhalten über den Netzwerktab in den DevTools darstellen. Es werden nur die ersten drei von zehn Bildern geladen. Erst durch ein Scrollen nach unten lädt der Browser nach.
Priorisiertes Laden und CDN
Best Practices verlangen, die "kritischen Bilder", die sich im Viewport befinden, über das Attribut priority
als solche zu markieren. Sollte man das jedoch vergessen, hilft auch in diesem Fall die Direktive mit einer Warnmeldung. Sie wird jedoch nur während des Entwicklungsmodus (ng serve
) gezeigt.
@Component({
template: `<div *ngFor="let holiday of holidays; first as isFirst">
<h3>{{ holiday.title }}</h3>
<img
[rawSrc]="'/assets/' + holiday.imageUrl"
[alt]="holiday.title"
[priority]="isFirst ? 'priority' : 'false'"
width="1920"
height="1080"
/>
</div>`,
imports: [NgForOf, NgOptimizedImage],
standalone: true,
})
export class OptimizedComponent {
// ...
}
Listing 2: Standalone-Komponente mit NgOptimizedImage, die das erste Bild priorisiert
Darüber hinaus gibt es noch die Möglichkeit, Content Delivery Networks (CDN) anzugeben, die Bilderskalierungen automatisiert bereitstellen. Das lässt sich bei Angular über eine Loader-Funktion einstellen. Auch hier stellt Angular sicher, dass die CDN-URL mit preconnect
eingestellt wurde. Falls nicht, erscheint wiederum eine Warnmeldung.
@Component({
template: `<h2>Optimized Version with Cloudinary CDN</h2>
<div *ngFor="let holiday of holidays; first as isFirst">
<h3>{{ holiday.title }}</h3>
<img
[rawSrc]="'/angular-14-2/' + holiday.imageUrl"
[alt]="holiday.title"
[priority]="isFirst ? 'priority' : 'false'"
width="1920"
height="1080"
/>
</div>`,
imports: [NgForOf, NgOptimizedImage],
providers: [provideCloudinaryLoader("https://res.cloudinary.com/dhidasbqj")],
standalone: true,
})
export class CdnComponent {
holidays = holidays;
}
Listing 3: Komponente mit Bildern von Cloudify
Vor allem für Angular-Anwendungen, die sehr stark mit Bildern arbeiten, ist diese neue Direktive ein wahrer Segen. In den nächsten Releases sollen weitere Features folgen.
Router als Standalone API
Neben der neuen Direktive bringt Angular 14.2 auch Standalone APIs. Standalone Components (mit Pipes und Directives) tragen bekanntlich durch das Weglassen von NgModules zu einer Verringerung des Boilerplate-Codes bei. Es ist zwar schön, seinen eigenen Code ohne NgModules schreiben zu können, man trifft sie aber immer dann an, wenn man Funktionen des Frameworks wie RouterModule
, ReactiveFormsModule
, HttpClientModule
oder Drittbibliotheken verwendet.
Die Standalone API schickt sich an, Alternativimplementierungen zu den NgModule-basierten Funktionen anzubieten.
Mit Angular 14.2 wurde das RouterModule
samt Direktiven zu Standalone migriert. Das heißt, es ist jetzt nicht mehr notwendig, bei der Konfiguration der Routen RouterModule.forRoot
oder forChild
zu verwenden. Als Alternative gibt es die Funktion provideRouter
.
bootstrapApplication(AppComponent, {
providers: [
provideRouter(routes),
importProvidersFrom(
BrowserAnimationsModule,
// RouterModule.forRoot(routes), <-- durhc provideRouter (oben) ersetzt
HttpClientModule
),
],
});
Listing 4: Die Funktion provideRouter im Einsatz
Auch die zum Routing dazugehörigen Direktiven wie routerLink
gibt es nun als Standalone-Variante. Man kann jetzt bei entsprechender Verwendung statt des RouterModule
einfach RouterLinkWithHref
in den imports
hinzufügen und die Direktive ist einsatzbereit.
@Component({
template: `<ul>
<li>
<a mat-raised-button routerLink="/optimized">Optimized</a>
</li>
</ul> `,
styleUrls: ["./sidemenu.component.scss"],
standalone: true,
imports: [CommonModule, MatButtonModule, RouterLinkWithHref],
})
export class SidemenuComponent {}
Listing 5: Komponente mit Standalone routerLink
Ein rundes Release
In Summe ist Angular 14.2 eine runde Sache und enthält vor allem durch NgOptimizedImage
neue Features in einem Umfang, den man selbst bei früheren Major Releases nicht hatte. Ein Upgrade lohnt sich daher auf alle Fälle.
Die Änderungen in Angular 14.2 sind im GitHub-Repository aufgeführt.
Weiterführende Links
- Using Angular NgOptimizedImage to Implement Image Loading Best Practices
- Defining the Core Web Vitals Metrics Thresholds
- What's new in Angular 14.2?
- Using the Angular Router API without RouterModule
- Optimizing Images with the Angular Image Directive
- Demo-Anwendung des Autors zu Angular 14.2
Rainer Hahnekamp
ist Trainer und Berater im Expertennetzwerk von AngularArchitects.io und dort für Schulungen rund um Angular verantwortlich. Darüber hinaus gibt er mit ng-news auf YouTube einen wöchentlichen Kurzüberblick über relevante Ereignisse im Angular-Umfeld.
(mai)