Ressourcen in JavaScript-Frameworks einbinden

Verbesserung von Largest Contentful Paint im gesamten JavaScript-Ökosystem.

Im Rahmen des Projekts Aurora hat Google mit beliebten Webframeworks zusammengearbeitet, um eine gute Leistung gemäß den Core Web Vitals zu gewährleisten. Angular und Next.js unterstützen bereits das Einbetten von Schriftarten, wie im ersten Teil dieses Artikels erläutert. Die zweite Optimierung, die wir behandeln, ist wichtiges CSS-Inlining, das jetzt standardmäßig in Angular CLI aktiviert ist und in Nuxt.js noch in der Entwicklung ist.

Schriftart-Inline-Anzeige

Nach der Analyse von Hunderten von Anwendungen stellte das Aurora-Team fest, dass Entwickler häufig Schriftarten in ihre Anwendungen einbinden, indem sie im <head>-Element von index.html darauf verweisen. Hier ein Beispiel für die Verwendung von Material Icons:

<!doctype html>
<html lang="en">
<head>
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
  ...
</html>

Obwohl dieses Muster völlig gültig und funktionsfähig ist, blockiert es das Rendern der Anwendung und führt zu einer zusätzlichen Anfrage. Sehen wir uns den Quellcode des Stylesheets an, auf das in der obigen HTML-Datei verwiesen wird, um das Problem besser zu verstehen:

/* fallback */
@font-face {
  font-family: 'Material Icons';
  font-style: normal;
  font-weight: 400;
  src: url(https://fonts.gstatic.com/font.woff2) format('woff2');
}

.material-icons {
  /*...*/
}

Beachten Sie, wie die Definition font-face auf eine externe Datei verweist, die auf fonts.gstatic.com gehostet wird. Beim Laden der Anwendung muss der Browser zuerst das ursprüngliche Stylesheet herunterladen, auf das im Head verwiesen wird.

Ein Bild, das zeigt, wie die Website eine Anfrage an den Server senden und das externe Stylesheet herunterladen muss
Zuerst lädt die Website das Schrift-Stylesheet.

Als Nächstes lädt der Browser die woff2-Datei herunter und kann dann mit dem Rendern der Anwendung fortfahren.

Ein Bild, das die beiden Anfragen zeigt, eine für das Schriftart-Stylesheet und die zweite für die Schriftartdatei.
Als Nächstes wird eine Anfrage zum Laden der Schriftart gesendet.

Eine Möglichkeit zur Optimierung besteht darin, das ursprüngliche Stylesheet zum Zeitpunkt des Builds herunterzuladen und in index.html einzufügen. Dadurch wird bei der Laufzeit ein vollständiger Hin- und Rückweg zum CDN übersprungen, was die Blockierungszeit verkürzt.

Beim Erstellen der Anwendung wird eine Anfrage an das CDN gesendet. Dadurch wird das Stylesheet abgerufen und in die HTML-Datei eingefügt. Der Domain wird ein <link rel=preconnect> hinzugefügt. Mit dieser Technik erhalten wir das folgende Ergebnis:

<!doctype html>
<html lang="en">
<head>
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin >
  <style type="text/css">
  @font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/font.woff2) format('woff2');}.material-icons{/*...*/}</style>
  ...
</html>

Inlining von Schriftarten ist jetzt in Next.js und Angular verfügbar

Wenn Framework-Entwickler die Optimierung in den zugrunde liegenden Tools implementieren, können bestehende und neue Anwendungen sie leichter aktivieren und so das gesamte System verbessern.

Diese Verbesserung ist in Next.js v10.2 und Angular v11 standardmäßig aktiviert. Beide unterstützen das Einfügen von Google- und Adobe-Schriftarten. Angular wird Letzteres voraussichtlich in Version 12.2 einführen.

Sie finden die Implementierung von Font Inlining in Next.js auf GitHub. Sehen Sie sich auch das Video an, in dem diese Optimierung im Kontext von Angular erklärt wird.

Kritisches CSS einbetten

Eine weitere Verbesserung besteht darin, die Messwerte First Contentful Paint (FCP) und Largest Contentful Paint (LCP) durch das Einfügen von kritischem CSS zu verbessern. Das kritische CSS einer Seite umfasst alle Stile, die beim ersten Rendern verwendet wurden. Weitere Informationen zu diesem Thema finden Sie unter Nicht kritisches CSS verzögern.

Wir haben festgestellt, dass viele Anwendungen Stile synchron laden, wodurch das Rendern von Anwendungen blockiert wird. Eine schnelle Lösung besteht darin, die Stile asynchron zu laden. Anstatt die Scripts mit media="all" zu laden, legen Sie den Wert des Attributs media auf print fest. Nachdem das Laden abgeschlossen ist, ersetzen Sie den Attributwert durch all:

<link rel="stylesheet" href="..." media="print" onload="this.media='all'">

Dies kann jedoch zu einem Flackern von nicht formatierten Inhalten führen.

Die Seite flackert beim Laden der Stile.

Das Video oben zeigt das Rendern einer Seite, bei der die Stile asynchron geladen werden. Das Flimmern tritt auf, weil der Browser zuerst die Stile herunterlädt und dann die folgende HTML-Datei rendert. Sobald der Browser die Stile heruntergeladen hat, löst er das onload-Ereignis des Link-Elements aus, aktualisiert das media-Attribut in all und wendet die Stile auf das DOM an.

In der Zeit zwischen dem Rendern des HTML-Codes und der Anwendung der Stile enthält die Seite teilweise keine Stile. Wenn der Browser die Stile verwendet, tritt Flackern auf. Dies beeinträchtigt die Nutzererfahrung und führt zu Regressionen in Cumulative Layout Shift (CLS).

Das Einbetten von kritischem CSS in Kombination mit dem asynchronen Laden von Stilen kann das Ladeverhalten verbessern. Das Tool Critters ermittelt, welche Stile auf der Seite verwendet werden. Dazu werden die Auswahlen in einem Stylesheet mit dem HTML-Code abgeglichen. Wenn eine Übereinstimmung gefunden wird, werden die entsprechenden Stile als Teil des kritischen CSS betrachtet und inline eingefügt.

Beispiel:

Don'ts
<head>
   <link rel="stylesheet" href="/styles.css" media="print" onload="this.media='all'">
</head>
<body>
  <section>
    <button class="primary"></button>
  </section>
</body>
/* styles.css */
section button.primary {
  /* ... */
}
.list {
  /* ... */
}

Beispiel vor dem Einfügen

Im Beispiel oben lesen und parsen Critter den Inhalt von styles.css. Anschließend gleichen sie die beiden Auswahlen mit dem HTML-Code ab und stellen fest, dass wir section button.primary verwenden. Zum Schluss werden die entsprechenden Stile in das <head>-Element der Seite eingefügt, was zu folgendem Ergebnis führt:

Do
<head>
  <link rel="stylesheet" href="/styles.css" media="print" onload="this.media='all'">
  <style>
  section button.primary {
    /* ... */
  }
  </style>
</head>
<body>
  <section>
    <button class="primary"></button>
  </section>
</body>

Beispiel nach dem Inline-Format.

Nachdem Sie das kritische CSS in den HTML-Code eingefügt haben, ist das Flackern der Seite verschwunden:

Das Laden der Seite nach dem Einfügen von CSS.

Das Einfügen von kritischen CSS-Dateien ist jetzt in Angular verfügbar und in Version 12 standardmäßig aktiviert. Wenn Sie Version 11 verwenden, aktivieren Sie die Funktion, indem Sie in angular.json das Attribut inlineCritical auf true setzen. Wenn Sie diese Funktion in Next.js aktivieren möchten, fügen Sie experimental: { optimizeCss: true } zu next.config.js hinzu.

Schlussfolgerungen

In diesem Beitrag haben wir uns mit der Zusammenarbeit zwischen Chrome und Web-Frameworks befasst. Wenn Sie ein Framework-Entwickler sind und einige der Probleme, die wir in Ihrer Technologie angegangen sind, wiedererkennen, hoffen wir, dass unsere Ergebnisse Sie dazu inspirieren, ähnliche Leistungsoptimierungen vorzunehmen.

Weitere Informationen zu den Verbesserungen Eine umfassende Liste der Optimierungen, die wir für Core Web Vitals vorgenommen haben, finden Sie im Beitrag Introducing Aurora.