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.

Lesezeit: 5 Min.
In Pocket speichern
vorlesen Druckansicht Kommentare lesen

(Bild: metamorworks/Shutterstock.com)

Von
  • 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.

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.

DevTools/Network: ohne NgOptimizedImage

DevTools/Network: mit NgOptimizedImage

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.

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

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.

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)