Het laden van scripts van derden optimaliseren in Next.js

Inzicht krijgen in de visie achter de scriptcomponent van Next.js, die 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, vertraging en laadtijd van scripts van derden kunnen de prestaties van een site aanzienlijk beïnvloeden. De Next.js Script-component bevat ingebouwde best practices en standaardinstellingen waarmee ontwikkelaars scripts van derden in hun apps kunnen introduceren en tegelijkertijd mogelijke prestatieproblemen kunnen aanpakken.

Scripts van derden en hun impact op de prestaties

Met scripts van derden kunnen webontwikkelaars bestaande oplossingen gebruiken om veelvoorkomende functies te implementeren en de ontwikkeltijd te verkorten. De makers van deze scripts hebben meestal geen reden om rekening te houden met de impact op de prestaties van de website die ze gebruikt. Deze scripts zijn ook een black box voor ontwikkelaars die ze gebruiken.

Scripts zijn verantwoordelijk voor een aanzienlijk aantal bytes van derden die door websites worden gedownload in verschillende categorieën verzoeken van derden. De browser geeft standaard prioriteit aan scripts op basis van hun positie in het document. Hierdoor kan de ontdekking of uitvoering van scripts die essentieel zijn voor de gebruikerservaring worden vertraagd.

Bibliotheken van derden die vereist zijn voor de vormgeving, moeten vroeg worden geladen om de pagina weer te geven. Derden die niet vereist zijn voor de eerste keer renderen, moeten worden uitgesteld, zodat ze andere verwerkingen in de hoofdthread niet blokkeren. Lighthouse heeft 2 controles om scripts te markeren die de weergave of de hoofdthread blokkeren.

Lighthouse-controles voor Bronnen verwijderen die de weergave blokkeren en Gebruik door derden minimaliseren

Het is belangrijk om na te denken over de volgorde waarin bronnen op je pagina worden geladen, zodat essentiële bronnen niet worden vertraagd en niet-essentiële bronnen geen essentiële bronnen blokkeren.

Hoewel er best practices zijn om de impact van derden te beperken, weet niet iedereen hoe ze deze moeten implementeren voor elke derde die ze gebruiken. Dit kan ingewikkeld zijn omdat:

  • Websites gebruiken gemiddeld 21 tot 23 verschillende derden, waaronder scripts, op mobiele apparaten en desktops. Het gebruik en de aanbevelingen kunnen voor elk verschillen.
  • De implementatie van veel derden kan verschillen, afhankelijk van of er een bepaald framework of een bepaalde UI-bibliotheek wordt gebruikt.
  • Er worden regelmatig nieuwere bibliotheken van derden geïntroduceerd.
  • Verschillende bedrijfsvereisten met betrekking tot dezelfde derde maken het voor ontwikkelaars moeilijk om het gebruik ervan te standaardiseren.

De focus van Aurora op scripts van derden

Een deel van de samenwerking van Aurora met open source webframeworks en -tools is om sterke standaardinstellingen en eigenzinnige tools te bieden 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 de statistieken voor Core Web Vitals te verbeteren.

Een van de belangrijkste stappen om ons doel te bereiken om de prestaties van frameworks te verbeteren, was onderzoek naar de ideale laadvolgorde van scripts van derden in Next.js. Frameworks zoals Next.js zijn bij uitstek geschikt om nuttige standaardinstellingen en functies te bieden waarmee ontwikkelaars bronnen, waaronder bronnen van derden, efficiënt kunnen laden. We hebben uitgebreide HTTP Archive- en Lighthouse-gegevens bestudeerd om te achterhalen welke derden de meeste weergaveblokkeringen veroorzaken in verschillende frameworks.

We hebben de Script-component ontwikkeld om het probleem op te lossen van scripts van derden die de hoofdthread blokkeren en in een app worden gebruikt. De component omvat volgordefuncties om ontwikkelaars betere beheeropties te bieden voor het laden van scripts van derden.

Scripts van derden sequencen zonder frameworkcomponent

De beschikbare richtlijnen om de impact van scripts die de weergave blokkeren te verminderen, bieden de volgende methoden om scripts van derden efficiënt te laden en te ordenen:

  1. Gebruik het kenmerk async of defer met <script>-tags die de browser vertellen niet-kritieke scripts van derden te laden zonder de documentparser te blokkeren. Scripts die niet vereist 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. Breng vroege verbindingen met vereiste oorsprongen tot stand met preconnect en dns-prefetch. Zo kunnen essentiële 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 vertraagd nadat de content van de hoofdpagina is geladen of wanneer de gebruiker omlaag scrollt naar het gedeelte van de pagina waar ze zijn opgenomen.

De Next.js-scriptcomponent

De Next.js-scriptcomponent implementeert de bovenstaande methoden voor het sequencen van scripts en biedt een template waarmee ontwikkelaars hun laadstrategie kunnen definiëren. Nadat de geschikte strategie is aangegeven, wordt de container optimaal geladen zonder andere essentiële bronnen te blokkeren.

De scriptcomponent is gebaseerd op de HTML-tag <script> en biedt een optie om de laadprioriteit voor scripts van derden in te stellen met het kenmerk strategy.

// 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 kenmerk strategy kan 3 waarden hebben.

  1. beforeInteractive: Deze optie kan worden gebruikt voor essentiële scripts die moeten worden uitgevoerd voordat de pagina interactief wordt. Next.js zorgt dat zulke scripts worden geïnjecteerd in de oorspronkelijke HTML op de server en worden uitgevoerd vóór andere zelfgebundelde JavaScript. Toestemmingsbeheer, scripts voor botdetectie of helperbibliotheken die vereist zijn om essentiële content weer te geven, zijn goede kandidaten voor deze strategie.

  2. afterInteractive: Dit is de standaardstrategie die wordt toegepast en is gelijk aan het laden van een script met het kenmerk defer. Gebruik dit voor scripts die de browser kan uitvoeren nadat de pagina interactief is, bijvoorbeeld analysescripts. Next.js injecteert deze scripts aan de clientzijde en ze worden uitgevoerd nadat de pagina is gehydrateerd. Tenzij anders aangegeven, worden alle scripts van derden die zijn gedefinieerd met de scriptcomponent, uitgesteld door Next.js. Dit biedt een sterke standaardinstelling.

  3. lazyOnload: Deze optie kan worden gebruikt om scripts met een lage prioriteit vertraagd te laden als de browser inactief is. De functionaliteit die door dergelijke scripts wordt geboden, is niet onmiddellijk vereist nadat de pagina interactief is geworden, bijvoorbeeld chat- of socialmedia-plug-ins.

Ontwikkelaars kunnen Next.js vertellen hoe hun app een script gebruikt door de strategie te specificeren. Zo kan het framework optimalisaties en best practices toepassen om het script te laden en tegelijkertijd de beste laadvolgorde te garanderen.

Met de scriptcomponent kunnen ontwikkelaars een script van derden overal in de app plaatsen voor laat ladende scripts van derden en op documentniveau voor kritieke scripts. Dit betekent dat de scriptcomponent zich op dezelfde locatie kan bevinden als de component die het script gebruikt. Na de hydratatie wordt het script geïnjecteerd in de kop van het oorspronkelijk gerenderde document of onderaan de hoofdtekst, afhankelijk van de gebruikte strategie.

De impact meten

We hebben de templates voor de Next.js-commerce-app en de starterblog gebruikt om 2 demo-apps te maken waarmee we de impact van scripts van derden konden meten. Veelgebruikte derden voor Google Tag Manager en social media-insluitingen werden eerst rechtstreeks op de pagina's van deze apps opgenomen en daarna via de scriptcomponent. Daarna hebben we de prestaties van deze pagina's vergeleken op WebPageTest.

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

Scripts van derden zijn toegevoegd aan de template van de handels-app voor de demo, zoals hieronder wordt beschreven.

Voor Na
Google Tag Manager met async Scriptcomponent met strategy = afterInteractive voor beide scripts
Twitter-volgknop zonder async of defer
Configuratie van script en scriptcomponent voor demo 1 met 2 scripts.

De volgende vergelijking toont de visuele voortgang voor beide versies van de Next.js commerce starter-kit. Zoals je ziet, vindt LCP bijna 1 seconde eerder plaats als de scriptcomponent aanstaat met de juiste laadstrategie.

Vergelijking van filmstrips die een verbetering in LCP toont

Scripts van derden in een Next.js-blog

Scripts van derden zijn toegevoegd aan de demo-blog-app, zoals hieronder wordt getoond.

Voor Na
Google Tag Manager met async Scriptcomponent met strategy = lazyonload voor elk van de 4 scripts
Twitter-volgknop met asynchroon
YouTube-abonneerknop zonder async of defer
LinkedIn-volgknop zonder async of defer
Configuratie van script en scriptcomponent voor demo 2 met 4 scripts.
Video die de voortgang van het laden van de indexpagina toont met en zonder de Script-component. De FCP is 0,5 seconden sneller met de scriptcomponent.

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

Wat is de volgende stap voor de scriptcomponent?

De strategieopties voor afterInteractive en lazyOnload bieden veel controle over scripts die de weergave blokkeren, maar we onderzoeken ook andere opties om de bruikbaarheid van de scriptcomponent te vergroten.

Web workers gebruiken

Web workers kunnen worden gebruikt om onafhankelijke scripts uit te voeren op achtergrondthreads. Zo kan de hoofdthread worden vrijgemaakt om taken voor de gebruikersinterface te verwerken en de prestaties te verbeteren. Web Workers zijn het meest geschikt om de verwerking van JavaScript, in plaats van UI-werk, te ontlasten van de primaire thread. Scripts die worden gebruikt voor support of marketing en die meestal geen interactie hebben met de UI, zijn goede kandidaten voor uitvoering in een achtergrondthread. Een lichte bibliotheek van derden, PartyTown, kan worden gebruikt om dergelijke scripts te isoleren in een webworker.

Met de huidige implementatie van de Next.js-scriptcomponent raden we je aan deze scripts uit te stellen in de hoofdthread door de strategie in te stellen op afterInteractive of lazyOnload. In de toekomst willen we een nieuwe strategieoptie introduceren, 'worker', waarmee Next.js PartyTown of een aangepaste oplossing kan gebruiken om scripts uit te voeren op web workers. We horen graag de opmerkingen van ontwikkelaars over dit RFC.

CLS minimaliseren

Insluitingen van derden, zoals advertenties, video's of socialmedia-feeds, kunnen indelingsverschuivingen veroorzaken als ze vertraagd worden geladen. Dit heeft invloed op de gebruikerservaring en de statistiek Cumulative Layout Shift (CLS) voor de pagina. Je kunt de CLS minimaliseren door het formaat van de container aan te geven waarin de insluiting wordt geladen.

De scriptcomponent kan worden gebruikt om insluitingen te laden die indelingsverschuivingen kunnen veroorzaken. We overwegen de functie uit te breiden met configuratieopties om de CLS te verlagen. Deze kan beschikbaar worden gemaakt in de scriptcomponent zelf of als begeleidende component.

Omslagmodule-componenten

De syntaxis en laadstrategie voor populaire scripts van derden, zoals Google Analytics of Google Tag Manager (GTM), liggen meestal vast. Deze kunnen verder worden ingekapseld in afzonderlijke wrapper-componenten voor elk type script. Alleen een minimale set app-specifieke kenmerken (zoals tracking-ID) is beschikbaar voor ontwikkelaars. Wrapper-componenten helpen ontwikkelaars op de volgende manieren:

  1. Zo kunnen ze makkelijker populaire scripttags opnemen.
  2. Zorgen dat het framework achter de schermen de meest optimale strategie gebruikt.

Conclusie

Scripts van derden worden meestal gemaakt om specifieke functies op te nemen in de website die ze gebruikt. We raden je aan niet-kritieke scripts uit te stellen om de impact ervan te verminderen. De Next.js Script-component doet dit standaard. Ontwikkelaars kunnen erop vertrouwen dat opgenomen scripts de essentiële functionaliteit niet vertragen, tenzij ze expliciet de strategie beforeInteractive toepassen. Net als de Next.js-scriptcomponent kunnen frameworkontwikkelaars ook overwegen deze functies in andere frameworks te bouwen. We onderzoeken actief of we een vergelijkbare component kunnen lanceren met het Nuxt.js-team. Op basis van feedback hopen we de scriptcomponent verder te verbeteren voor aanvullende use cases.

Erkenning

Met dank aan Kara Erickson, Janicklas Ralph, Katie Hempenius, Philip Walton, Jeremy Wagner en Addy Osmani voor hun feedback op deze post.