Het optimaliseren van het laden van scripts van derden in Next.js

Begrijp de visie achter het Script-component van Next.js, dat een ingebouwde oplossing biedt om het laden van scripts van derden te optimaliseren.

Leena Sohoni
Leena Sohoni
Houssein Djirdeh
Houssein Djirdeh

Ongeveer 45% van de verzoeken van websites die op mobiele apparaten en desktops worden weergegeven, zijn verzoeken van derden, waarvan 33% scripts zijn . De grootte, latentie en laadsnelheid van scripts van derden kunnen de prestaties van een site aanzienlijk beïnvloeden. De Next.js Script-component wordt geleverd met ingebouwde best practices en standaardinstellingen om ontwikkelaars te helpen scripts van derden in hun applicaties te implementeren en tegelijkertijd potentiële prestatieproblemen direct op te lossen.

Scripts van derden en hun impact op de prestaties

Scripts van derden stellen webontwikkelaars in staat om bestaande oplossingen te gebruiken om gemeenschappelijke functies te implementeren en de ontwikkeltijd te verkorten. De makers van deze scripts hebben echter doorgaans geen enkele reden om rekening te houden met de impact op de prestaties van de website. Deze scripts vormen bovendien een blackbox voor ontwikkelaars die ze gebruiken.

Scripts zijn verantwoordelijk voor een aanzienlijk aantal bytes van derden die door websites worden gedownload in verschillende categorieën van verzoeken van derden. Standaard geeft de browser prioriteit aan scripts op basis van hun positie in het document, wat de detectie of uitvoering van scripts die cruciaal zijn voor de gebruikerservaring kan vertragen.

Bibliotheken van derden die nodig zijn voor de lay-out, moeten vroeg worden geladen om de pagina te kunnen renderen. Bibliotheken van derden die niet nodig zijn voor de eerste render, moeten worden uitgesteld om te voorkomen dat ze andere verwerking op de hoofdthread blokkeren. Lighthouse heeft twee audits om scripts te markeren die de render blokkeren of die de hoofdthread blokkeren.

Lighthouse-audits voor het elimineren van render-blokkerende bronnen en het minimaliseren van gebruik door derden

Het is belangrijk om rekening te houden met de volgorde waarin bronnen op uw pagina worden geladen, zodat kritieke bronnen geen vertraging oplopen en niet-kritieke bronnen geen kritieke bronnen blokkeren.

Hoewel er best practices bestaan ​​om de impact van externe partijen te verminderen, weet niet iedereen hoe ze deze moeten implementeren voor elke externe partij die ze gebruiken. Dit kan ingewikkeld zijn omdat:

  • Websites gebruiken gemiddeld 21 tot 23 verschillende derde partijen – inclusief scripts – op mobiele apparaten en desktops. Het gebruik en de aanbevelingen kunnen per partij verschillen.
  • De implementatie van veel derde partijen kan verschillen, afhankelijk van of een bepaald framework of UI-bibliotheek wordt gebruikt.
  • Er worden regelmatig nieuwe bibliotheken van derden geïntroduceerd.
  • Doordat de bedrijfsvereisten voor dezelfde externe partij uiteenlopen, is het voor ontwikkelaars lastig om het gebruik ervan te standaardiseren.

Aurora's focus op scripts van derden

Onderdeel van Aurora's samenwerking met open source webframeworks en -tools is het bieden van sterke standaarden en eigenzinnige tools waarmee ontwikkelaars aspecten van de gebruikerservaring kunnen verbeteren, zoals prestaties, toegankelijkheid, beveiliging en mobiele gereedheid. In 2021 hebben we ons gericht op het helpen van framework stacks om de gebruikerservaring en hun Core Web Vitals -statistieken te verbeteren.

Een van de belangrijkste stappen in het bereiken van ons doel om de prestaties van frameworks te verbeteren, was het onderzoeken van de ideale laadvolgorde van scripts van derden in Next.js. Frameworks zoals Next.js zijn uniek gepositioneerd om nuttige standaardinstellingen en functies te bieden die ontwikkelaars helpen resources, inclusief die van derden, efficiënt te laden. We hebben uitgebreide data van HTTP Archive en Lighthouse bestudeerd om te achterhalen welke externe partijen rendering het meest blokkeren in verschillende frameworks.

Om het probleem aan te pakken waarbij de hoofdthread scripts van derden blokkeert die in een applicatie worden gebruikt, hebben we de Script-component gebouwd. Deze component bevat sequentiefuncties om ontwikkelaars betere controle te geven over het laden van scripts van derden.

Sequencing van scripts van derden zonder een frameworkcomponent

De beschikbare richtlijnen om de impact van render-blokkerende scripts te verminderen, bieden de volgende methoden voor het efficiënt laden en sequensen van scripts van derden:

  1. Gebruik het kenmerk async of defer met <script> -tags om de browser te vertellen niet-kritieke scripts van derden te laden zonder de documentparser te blokkeren. Scripts die niet nodig zijn voor het laden van de eerste pagina of de eerste gebruikersinteractie, kunnen als niet-kritiek worden beschouwd.

       <script src="https://example.com/script1.js" defer></script>
       <script src="https://example.com/script2.js" async></script>
    
  2. Maak vroegtijdige verbindingen met de vereiste bronnen met behulp van preconnect en dns-prefetch. Hierdoor kunnen kritieke scripts eerder worden gedownload.

       <head>
           <link rel="preconnect" href="http://PreconnThis.com">
           <link rel="dns-prefetch" href="http://PrefetchThis.com">
       </head>
    
  3. Laad bronnen en insluitingen van derden pas nadat de inhoud van de hoofdpagina is geladen of wanneer de gebruiker naar beneden scrolt naar het deel van de pagina waar ze zijn opgenomen.

Het Next.js Script-component

De Next.js Script-component implementeert de bovenstaande methoden voor het sequencen van scripts en biedt ontwikkelaars een sjabloon waarmee ze hun laadstrategie kunnen definiëren. Zodra de geschikte strategie is gespecificeerd, wordt deze optimaal geladen zonder andere kritieke bronnen te blokkeren.

Het Script-onderdeel bouwt voort op de HTML <script>-tag en biedt een optie om de laadprioriteit voor scripts van derden in te stellen met behulp van het strategy-kenmerk.

// Example for beforeInteractive:
<Script src="https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?features=IntersectionObserverEntry%2CIntersectionObserver" strategy="beforeInteractive" />

// Example for afterInteractive (default):
<Script src="https://example.com/samplescript.js" />

// Example for lazyonload:
<Script src="https://connect.facebook.net/en_US/sdk.js" strategy="lazyOnload" />

Het strategiekenmerk kan drie waarden aannemen.

  1. beforeInteractive : Deze optie kan worden gebruikt voor kritieke scripts die moeten worden uitgevoerd voordat de pagina interactief wordt. Next.js zorgt ervoor dat dergelijke scripts in de initiële HTML op de server worden geïnjecteerd en vóór andere zelfgebundelde JavaScript-code worden uitgevoerd. Toestemmingsbeheer, botdetectiescripts of hulpbibliotheken die nodig zijn om kritieke content weer te geven, zijn goede kandidaten voor deze strategie.

  2. afterInteractive : Dit is de standaardstrategie die wordt toegepast en is equivalent aan het laden van een script met het kenmerk defer. Deze strategie dient te worden gebruikt voor scripts die de browser kan uitvoeren nadat de pagina interactief is, bijvoorbeeld analysescripts. Next.js injecteert deze scripts aan de clientzijde en voert ze uit nadat de pagina is gehydrateerd. Tenzij anders aangegeven, worden alle scripts van derden die met de Script-component zijn gedefinieerd, uitgesteld door Next.js, wat een sterke standaard biedt.

  3. lazyOnload : Deze optie kan worden gebruikt om scripts met lage prioriteit lazy te laden wanneer de browser inactief is. De functionaliteit die dergelijke scripts bieden, is niet direct nodig nadat de pagina interactief wordt, bijvoorbeeld chat of plug-ins voor sociale media.

Ontwikkelaars kunnen Next.js vertellen hoe hun applicatie een script gebruikt door de strategie te specificeren. Dit stelt het framework in staat om optimalisaties en best practices toe te passen om het script te laden en tegelijkertijd de beste laadvolgorde te garanderen.

Met de Script-component kunnen ontwikkelaars een script van derden overal in de applicatie plaatsen voor laat geladen scripts van derden en op documentniveau voor kritieke scripts. Dit betekent dat de Script-component samen met het component dat het script gebruikt, kan worden geplaatst. Na hydratatie wordt het script in de kop van het oorspronkelijk gerenderde document of onderaan de body ingevoegd, afhankelijk van de gebruikte strategie.

Het meten van de impact

We gebruikten de templates voor de Next.js commerce-app en de startersblog om twee demo-apps te maken waarmee we de impact van het toevoegen van scripts van derden konden meten. Veelgebruikte scripts van derden voor Google Tag Manager en social media-embeds werden eerst rechtstreeks en vervolgens via de scriptcomponent op de pagina's van deze apps opgenomen. Vervolgens vergeleken we de prestaties van deze pagina's met WebPageTest .

Scripts van derden in een Next.js-commerce-app

Voor de demo zijn scripts van derden toegevoegd aan de sjabloon voor de commerciële app, zoals hieronder weergegeven.

Voor Na
Google Tag Manager met asynchrone Scriptcomponent met strategie = afterInteractive voor beide scripts
Twitter Volgen-knop zonder asynchrone of uitgestelde weergave
Script- en scriptcomponentconfiguratie voor demo 1 met 2 scripts.

De volgende vergelijking toont de visuele voortgang voor beide versies van de Next.js Commerce Starter Kit . Zoals te zien is, vindt LCP bijna 1 seconde eerder plaats met het scriptcomponent ingeschakeld met de juiste laadstrategie.

Vergelijking van filmstroken toont verbetering in LCP

Scripts van derden in een Next.js-blog

Aan de demo-blog-app zijn scripts van derden toegevoegd, zoals hieronder aangegeven.

Voor Na
Google Tag Manager met asynchrone Scriptcomponent met strategie = lazyonload voor elk van de vier scripts
Twitter Volgknop met asynchrone
YouTube-abonneerknop zonder asynchrone of uitgestelde weergave
LinkedIn Volgknop zonder asynchrone of uitgestelde weergave
Script- en scriptcomponentconfiguratie voor demo 2 met 4 scripts.
Video die de laadvoortgang van de indexpagina met en zonder het scriptcomponent toont. Er is een verbetering van 0,5 seconde in FCP met het scriptcomponent.

Zoals te zien is in de video, vindt First Contentful Paint (FCP) plaats na 0,9 seconden op de pagina zonder het scriptcomponent en na 0,4 seconden met het scriptcomponent.

Wat is de volgende stap voor het Script-component?

Hoewel de strategieopties voor afterInteractive en lazyOnload aanzienlijke controle bieden over scripts die het renderen blokkeren, onderzoeken we ook andere opties die de bruikbaarheid van het Script-onderdeel zouden vergroten.

Webworkers gebruiken

Webworkers kunnen worden gebruikt om onafhankelijke scripts op achtergrondthreads uit te voeren, waardoor de hoofdthread vrijgemaakt wordt voor het verwerken van taken in de gebruikersinterface en de prestaties worden verbeterd. Webworkers zijn het meest geschikt om JavaScript-verwerking, in plaats van UI-werk, van de hoofdthread af te halen. Scripts die worden gebruikt voor klantondersteuning of marketing, en die doorgaans geen interactie hebben met de gebruikersinterface, zijn mogelijk geschikt voor uitvoering op een achtergrondthread. Een eenvoudige bibliotheek van derden, PartyTown , kan worden gebruikt om dergelijke scripts in een webworker te isoleren.

Met de huidige implementatie van de Next.js-scriptcomponent raden we aan deze scripts uit te stellen op de hoofdthread door de strategie in te stellen op afterInteractive of lazyOnload . In de toekomst stellen we voor om een ​​nieuwe strategieoptie te introduceren, 'worker' , waarmee Next.js PartyTown of een aangepaste oplossing kan gebruiken om scripts op webworkers uit te voeren. We stellen reacties van ontwikkelaars op deze RFC op prijs.

CLS minimaliseren

Embeds van derden, zoals advertenties, video's of socialemediafeeds, kunnen lay-outverschuivingen veroorzaken wanneer ze lazyloaded worden. Dit beïnvloedt de gebruikerservaring en de cumulatieve lay-outverschuiving (CLS) van de pagina. CLS kan worden geminimaliseerd door de grootte van de container te specificeren waar de embed wordt geladen.

De Script-component kan worden gebruikt om embeds te laden die lay-outverschuivingen kunnen veroorzaken. We overwegen om deze uit te breiden met configuratieopties die de CLS helpen verminderen. Dit kan beschikbaar worden gesteld binnen de Script-component zelf of als een begeleidende component.

Wrapper-componenten

De syntaxis en laadstrategie voor het opnemen van populaire scripts van derden, zoals Google Analytics of Google Tag Manager (GTM), liggen meestal vast. Deze kunnen verder worden ingekapseld in individuele wrappercomponenten voor elk type script. Slechts een minimale set applicatiespecifieke kenmerken (zoals tracking-ID) is beschikbaar voor ontwikkelaars. Wrappercomponenten helpen ontwikkelaars door:

  1. Zodat ze makkelijker populaire scripttags kunnen opnemen.
  2. Zorgt ervoor dat het framework de meest optimale strategie onder de motorkap gebruikt.

Conclusie

Scripts van derden worden meestal gemaakt om specifieke functies in de gebruikende website op te nemen. Om de impact van niet-kritieke scripts te verminderen, raden we aan deze uit te stellen – wat de Next.js Script-component standaard doet. Ontwikkelaars hebben de zekerheid dat de meegeleverde scripts kritieke functionaliteit niet zullen vertragen, tenzij ze expliciet de beforeInteractive strategie toepassen. Net als de Next.js Script-component kunnen frameworkontwikkelaars overwegen om deze functies ook in andere frameworks te bouwen. We onderzoeken actief de mogelijkheden om een ​​vergelijkbare component te integreren met het Nuxt.js-team. Op basis van feedback hopen we de Script-component verder te verbeteren om aanvullende use cases te dekken.

Dankbetuigingen

Hartelijk dank aan Kara Erickson , Janicklas Ralph , Katie Hempenius , Philip Walton , Jeremy Wagner en Addy Osmani voor hun feedback op dit bericht.