Ressourcen in JavaScript-Frameworks einbinden

Optimierung von Largest Contentful Paint in der JavaScript-Umgebung

Im Rahmen des Projekts Aurora arbeitet Google mit beliebten Web-Frameworks, damit diese gemäß den Core Web Vitals eine gute Leistung erzielen. Angular und Next.js haben bereits ein Font-Inline-Objekt, wie im ersten Teil dieses Artikels erläutert. Die zweite Optimierung, die wir behandeln, ist das kritische CSS-Inline-Element, das jetzt standardmäßig in Angular CLI aktiviert ist und die Implementierung in Nuxt.js noch in Arbeit ist.

Font-Inline-Anzeige

Das Aurora-Team analysierte Hunderte von Anwendungen und stellte fest, dass Entwickler oft Schriftarten in ihre Anwendungen aufnehmen, indem sie im <head>-Element von index.html darauf verweisen. Hier ist ein Beispiel dafür, wie das mit Material Icons aussehen würde:

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

Obwohl dieses Muster vollständig gültig und funktionsfähig ist, blockiert es das Rendering der Anwendung und stellt eine zusätzliche Anfrage her. Um das Problem besser zu verstehen, schauen Sie sich den Quellcode des Stylesheets an, auf das im HTML-Code oben verwiesen wird:

/* 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 {
  /*...*/
}

Die Definition font-face verweist auf eine externe Datei, die auf fonts.gstatic.com gehostet wird. Beim Laden der Anwendung muss der Browser zuerst das ursprüngliche Stylesheet herunterladen, auf das im head-Element 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 Schriftart-Stylesheet.

Als Nächstes lädt der Browser die Datei woff2 herunter. Schließlich kann er mit dem Rendern der Anwendung fortfahren.

Ein Bild, auf dem die beiden Anfragen zu sehen sind: eine für das Schriftart-Stylesheet und die zweite für die Schriftartdatei.
Als Nächstes wird das Laden der Schriftart angefordert.

Eine Möglichkeit zur Optimierung besteht darin, das ursprüngliche Stylesheet zum Zeitpunkt der Erstellung herunterzuladen und in index.html einzufügen. Dadurch wird zur Laufzeit ein gesamter Umlauf zum CDN übersprungen, wodurch die Blockierzeit reduziert wird.

Beim Erstellen der Anwendung wird eine Anfrage an das CDN gesendet. Dieses ruft das Stylesheet ab, fügt es inline in die HTML-Datei ein und fügt der Domain ein <link rel=preconnect> hinzu. Mit dieser Technik würden wir das folgende Ergebnis erhalten:

<!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>

Font-Inlineing jetzt in Next.js und Angular verfügbar

Wenn Framework-Entwickler die Optimierung in den zugrunde liegenden Tools implementieren, erleichtern sie es vorhandenen und neuen Anwendungen, diese zu aktivieren, wodurch die gesamte Umgebung verbessert wird.

Diese Verbesserung ist ab Next.js v10.2 und Angular v11 standardmäßig aktiviert. Beide unterstützen das Inlineing von Google- und Adobe-Schriftarten. Für Angular wird Letzteres voraussichtlich in Version 12.2 eingeführt.

Informationen zur Implementierung von Font Inlineing in Next.js auf GitHub finden Sie auch in dem Video, in dem diese Optimierung im Kontext von Angular erläutert wird.

Kritisches CSS einbinden

Eine weitere Verbesserung betrifft die Messwerte First Contentful Paint (FCP) und Largest Contentful Paint (LCP), indem kritisches CSS eingebunden wird. Der entscheidende CSS-Code einer Seite umfasst alle Stile, die beim ersten Rendering verwendet wurden. Weitere Informationen zu diesem Thema finden Sie unter Nicht kritisches CSS verschieben.

Wir haben festgestellt, dass viele Anwendungen Stile synchron laden, was das Rendern von Anwendungen blockiert. Eine schnelle Lösung besteht darin, die Stile asynchron zu laden. Anstatt die Skripts mit media="all" zu laden, legen Sie den Wert des Attributs media auf print fest. Ersetzen Sie nach Abschluss des Ladevorgangs den Attributwert durch all:

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

Dies kann jedoch bei nicht formatierten Inhalten zu Flimmern führen.

Die Seite scheint beim Laden der Stile zu flackern.

Im Video oben wird das Rendern einer Seite gezeigt, wobei die Stile asynchron geladen werden. Dieses Flackern tritt auf, weil der Browser zuerst mit dem Herunterladen der Stile beginnt und dann den HTML-Code rendert. Sobald der Browser die Stile herunterlädt, wird das onload-Ereignis des Link-Elements ausgelöst, das media-Attribut auf all aktualisiert und die Stile auf das DOM angewendet.

In der Zeit zwischen dem Rendern des HTML-Codes und dem Anwenden der Stile ist die Seite teilweise unverändert. Wenn der Browser die Stile verwendet, tritt ein Flackern auf. Dies beeinträchtigt die Nutzerfreundlichkeit und führt zu Regressionen beim Cumulative Layout Shift (CLS).

Kritisches CSS-Inlineing sowie das asynchrone Laden von Stilen können das Ladeverhalten verbessern. Das critters-Tool findet die auf der Seite verwendeten Stile, indem es die Selektoren in einem Stylesheet betrachtet und sie mit dem HTML-Code abgleicht. Wenn eine Übereinstimmung gefunden wird, werden die entsprechenden Stile als Teil des wichtigen CSS-Codes betrachtet und eingebettet.

Sehen wir uns ein Beispiel an:

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 Inline-Objekt.

Im obigen Beispiel lesen und parsen die Tiere den Inhalt von styles.css. Anschließend werden die beiden Selektoren mit dem HTML-Code abgeglichen und erkannt, dass wir section button.primary verwenden. Schließlich werden die Tiere die entsprechenden Stile in den <head> der Seite inline platzieren, was zu folgendem Ergebnis führt:

Das sollten Sie tun:
<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-Objekt.

Nachdem Sie den wichtigen CSS-Code in den HTML-Code eingefügt haben, werden Sie feststellen, dass das Flackern der Seite nicht mehr angezeigt wird:

Die Seite, die nach dem CSS-Inline-Element geladen wird.

Kritisches CSS-Inlineing ist jetzt in Angular verfügbar und in v12 standardmäßig aktiviert. Wenn du v11 verwendest, musst du sie aktivieren, indem du in angular.json die Eigenschaft inlineCritical auf true setzt. Wenn Sie diese Funktion in Next.js aktivieren möchten, fügen Sie experimental: { optimizeCss: true } zu next.config.js hinzu.

Ergebnisse

In diesem Beitrag haben wir einige der Zusammenarbeit zwischen Chrome- und Web-Frameworks besprochen. Wenn Sie ein Framework-Autor sind und einige der Probleme, die wir in Ihrer Technologie angegangen sind, erkennen, hoffen wir, dass unsere Ergebnisse Sie dazu inspirieren, ähnliche Leistungsoptimierungen anzuwenden.

Weitere Informationen zu den Verbesserungen Eine umfassende Liste der Optimierungen, die wir für Core Web Vitals durchgeführt haben, finden Sie im Beitrag Introducing Aurora (Einführung in Aurora).