Resource-inlining in JavaScript-frameworks

Verbetering van de grootste inhoudsvolle verf in het JavaScript-ecosysteem.

Als onderdeel van project Aurora heeft Google met populaire webframeworks gewerkt om ervoor te zorgen dat deze goed presteren volgens Core Web Vitals . Angular en Next.js hebben al font-inlining geïntroduceerd, wat in het eerste deel van dit artikel wordt uitgelegd. De tweede optimalisatie die we zullen behandelen is kritische CSS-inlining, die nu standaard is ingeschakeld in Angular CLI en een work in progress-implementatie heeft in Nuxt.js.

Lettertype inlining

Na honderden applicaties te hebben geanalyseerd, ontdekte het Aurora-team dat ontwikkelaars vaak lettertypen in hun applicaties opnemen door ernaar te verwijzen in het <head> -element van index.html . Hier is een voorbeeld van hoe dit eruit zou zien als u materiaalpictogrammen opneemt:

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

Ook al is dit patroon volledig geldig en functioneel, het blokkeert de weergave van de applicatie en introduceert een extra verzoek. Om beter te begrijpen wat er aan de hand is, kun je de broncode bekijken van het stylesheet waarnaar in de HTML hierboven wordt verwezen:

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

Merk op hoe de definitie font-face verwijst naar een extern bestand dat wordt gehost op fonts.gstatic.com . Bij het laden van de applicatie moet de browser eerst het originele stylesheet downloaden waarnaar in de head wordt verwezen.

Een afbeelding die laat zien hoe de website een verzoek moet indienen bij de server en de externe stylesheet moet downloaden
Eerst laadt de website het lettertypestylesheet.

Vervolgens downloadt de browser het woff2 bestand en kan uiteindelijk doorgaan met het renderen van de applicatie.

Een afbeelding die de twee ingediende verzoeken toont, één voor het lettertypestylesheet, de tweede voor het lettertypebestand.
Vervolgens wordt er gevraagd om het lettertype te laden.

Een mogelijkheid voor optimalisatie is om tijdens het bouwen de initiële stylesheet te downloaden en deze in index.html inline te plaatsen. Hierdoor wordt tijdens runtime een volledige retour naar het CDN overgeslagen, waardoor de blokkeertijd wordt verkort.

Bij het bouwen van de applicatie wordt er een verzoek naar het CDN gestuurd, deze haalt de stylesheet op en plaatst deze in het HTML-bestand, waarbij een <link rel=preconnect> aan het domein wordt toegevoegd. Als we deze techniek toepassen, krijgen we het volgende resultaat:

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

Lettertype-inlining is nu beschikbaar in Next.js en Angular

Wanneer raamwerkontwikkelaars optimalisatie implementeren in de onderliggende tool, maken ze het gemakkelijker voor bestaande en nieuwe applicaties om dit mogelijk te maken, waardoor de verbetering voor het hele ecosysteem wordt doorgevoerd.

Deze verbetering is standaard ingeschakeld vanaf Next.js v10.2 en Angular v11. Beide hebben ondersteuning voor inlining van Google- en Adobe-lettertypen. Angular verwacht dit laatste in v12.2 te introduceren.

Je kunt de implementatie van font inlining vinden in Next.js op GitHub , en bekijk de video waarin deze optimalisatie wordt uitgelegd in de context van Angular .

Kritische CSS inlinen

Een andere verbetering betreft het verbeteren van de First Contentful Paint (FCP) en Largest Contentful Paint (LCP) -statistieken door kritische CSS inline te plaatsen. De kritische CSS van een pagina omvat alle stijlen die bij de eerste weergave worden gebruikt. Voor meer informatie over dit onderwerp, ga naar Defer non-critical CSS .

We hebben vastgesteld dat veel applicaties stijlen synchroon laden, wat het renderen van applicaties blokkeert. Een snelle oplossing is om de stijlen asynchroon te laden. In plaats van de scripts te laden met media="all" , stelt u de waarde van het media attribuut in op print , en zodra het laden is voltooid, vervangt u de attribuutwaarde door all :

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

Deze praktijk kan echter leiden tot flikkering van ongestylede inhoud.

De pagina lijkt te flikkeren terwijl de stijlen worden geladen.

De video hierboven toont de weergave van een pagina, waarbij de stijlen asynchroon worden geladen. Het flikkeren gebeurt omdat de browser eerst de stijlen begint te downloaden en vervolgens de HTML weergeeft die volgt. Zodra de browser de stijlen heeft gedownload, activeert deze de onload gebeurtenis van het link-element, waarbij het media attribuut wordt bijgewerkt naar all , en worden de stijlen toegepast op de DOM.

Gedurende de tijd tussen het weergeven van de HTML en het toepassen van de stijlen is de pagina gedeeltelijk ongestyled. Wanneer de browser de stijlen gebruikt, zien we flikkeringen, wat een slechte gebruikerservaring is en resulteert in regressies in Cumulative Layout Shift (CLS) .

Kritieke CSS-inlining kan, samen met het asynchrone laden van stijlen, het laadgedrag verbeteren. De critters- tool ontdekt welke stijlen op de pagina worden gebruikt, door naar de selectors in een stylesheet te kijken en deze te vergelijken met de HTML. Wanneer het een overeenkomst vindt, beschouwt het de overeenkomstige stijlen als onderdeel van de kritische CSS en lijnt deze uit.

Laten we eens kijken naar een voorbeeld:

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

Voorbeeld vóór het inlinen.

In het bovenstaande voorbeeld zullen beestjes de inhoud van styles.css lezen en parseren, waarna het de twee selectors vergelijkt met de HTML en ontdekt dat we section button.primary gebruiken. Ten slotte zullen beestjes de overeenkomstige stijlen in de <head> van de pagina inline plaatsen, wat resulteert in:

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

Voorbeeld na inlining.

Na het inlinen van de kritische CSS in de HTML zul je merken dat het flikkeren van de pagina verdwenen is:

De pagina die wordt geladen na CSS-inlining.

Kritieke CSS-inlining is nu beschikbaar in Angular en standaard ingeschakeld in v12. Als u versie 11 gebruikt, schakelt u deze in door de eigenschap inlineCritical in te stellen op true in angular.json . Om u aan te melden voor deze functie in Next.js, voegt u experimental: { optimizeCss: true } toe aan uw next.config.js .

Conclusies

In dit bericht hebben we een deel van de samenwerking tussen Chrome en webframeworks besproken. Als u een raamwerkauteur bent en enkele van de problemen herkent die we in uw technologie hebben aangepakt, hopen we dat onze bevindingen u inspireren om vergelijkbare prestatie-optimalisaties toe te passen.

Lees meer over de verbeteringen . U kunt een uitgebreide lijst vinden van het optimalisatiewerk dat we hebben gedaan voor Core Web Vitals in de post Introductie van Aurora .