JavaScript-prioriteit optimaliseren voor snellere laadtijden

Leer hoe je scripts effectief prioriteert om de Core Web Vitals te verbeteren.

Arjen Karel Core Web Vitals Consultant
Arjen Karel - linkedin
Last update: 2024-12-12

JavaScript-prioriteiten beheren voor betere webprestaties

Eén ding is altijd duidelijk geweest: niet alle JavaScript is gelijk. Sommige scripts regelen kritieke interacties zoals 'menu-interactie' of 'toevoegen aan winkelwagen', terwijl andere scripts veel minder belangrijk zijn. Neem bijvoorbeeld je 'exit intent' pop-up script dat bezoekers die op het punt staan je site te verlaten uitnodigt om een vragenlijst in te vullen. Ik weet zeker dat we allemaal zonder die laatste kunnen, maar het zou erg moeilijk zijn om een website te navigeren zonder de eerste.

Toch wordt dit onderscheid op 'een gemiddelde website' op technisch niveau zelden gemaakt. Alle JavaScripts worden 'gewoon toegevoegd' aan de pagina en de browser moet het zelf maar uitzoeken. Dat is een probleem, want je browser heeft geen idee wat belangrijk is en wat niet. Wij, als developers, weten dat wel. Laten we dat dus oplossen!

Hoe JavaScript-prioriteit de Core Web Vitals kan beïnvloeden

Scripts zomaar aan de pagina toevoegen zonder de juiste overweging kan alle 3 de Core Web Vitals beïnvloeden. De Largest Contentful Paint, de Interaction to Next Paint en de Cumulative Layout Shift. 

javascript lcp impact example

Voorbeeld: de LCP network resource wordt vertraagd door render blocking JavaScripts

De Largest contentful Paint is gevoelig voor concurrentie om bandbreedte en CPU. Wanneer te veel scripts vechten om vroege netwerkbronnen, zal dit de Largest Contentful Paint network resource vertragen, en vroeg CPU-werk zal de LCP vertragen door de main thread te blokkeren.

De Interaction to Next Paint kan worden beïnvloed door scripts die vlak voor een interactie worden uitgevoerd. Wanneer scripts worden uitgevoerd, blokkeren ze de main thread en vertragen ze elke interactie tijdens die uitvoeringstijd.

Scripts kunnen ook een Cumulative Layout Shift veroorzaken als ze 'de lay-out van de pagina veranderen'. Advertentiescripts die banners in de pagina injecteren en sliders staan erom bekend dit te doen.

5 typen JavaScript-prioriteiten

Ik onderscheid graag 5 typen JavaScript-prioriteiten. Laten we die snel bespreken voordat we dieper ingaan.

  • Render Critical: dit zijn de slechtste scripts die je kunt hebben. Ze veranderen de lay-out van de pagina en zonder deze scripts te laden, zal de lay-out compleet anders zijn. Voorbeeld: sommige slider-scripts of een A/B-test.
  • Critical Scripts: Deze scripts regelen kritieke paginfunctionaliteit en zonder deze scripts zijn kritieke taken zoals een product toevoegen aan een winkelwagen, site search of navigatie niet mogelijk.
  • Important scripts. Deze scripts regelen belangrijke (bedrijfs)logica en je site is ervan afhankelijk. Bijvoorbeeld: Analytics.
  • Nice to have scripts. Deze scripts zijn leuk om te hebben, maar als het erop aankomt, hebben we ze niet echt nodig om de pagina te laten functioneren. Bijvoorbeeld een chat widget of een exit intent.
  • Future Scripts. Deze scripts kunnen kritiek of 'nice to have' zijn, maar we hebben ze nu niet nodig omdat er eerst 'andere stappen' moeten worden genomen voordat we ze daadwerkelijk kunnen gebruiken. Bijvoorbeeld een multi-stage check-out script.
Nu we een idee hebben van scriptprioriteiten, gaan we er dieper op in!

1. Render-Critical Scripts

Dit zijn de meest storende scripts, omdat ze direct invloed hebben op hoe de pagina wordt weergegeven. Zonder hen kan de lay-out breken of er drastisch anders uitzien dan het beoogde ontwerp. Voorbeelden zijn scripts voor sliders of A/B-testing frameworks die de lay-out vroeg in het laadproces veranderen. 

Het probleem met dit soort scripts is dat ze niet kunnen worden uitgesteld of vertraagd. Elke vertraging zal ervoor zorgen dat de lay-out van de website verschuift, wat een slechte UX veroorzaakt en de Core Web Vitals doet falen.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Page Title</title>
    <link href="styles.css" rel="stylesheet" />
    <script src="render-critical.js"></script>
  </head>
  <body></body>
</html>

Aanbevolen werkwijzen:

  • Vermijd render-critical scripts zoals deze waar mogelijk. Herschrijf je code om afhankelijkheid van dit soort scripts te voorkomen.
  • Als het niet te vermijden is, inline of laad alleen de absoluut noodzakelijke delen van deze scripts. 
  • Gebruik geen defer of async voor deze scripts en plaats ze bovenaan in de head om een 'zo vroeg mogelijke' download te triggeren.

2. Critical Scripts

Deze scripts maken fundamentele interacties mogelijk. Zonder hen worden kritieke taken zoals sitenavigatie, items toevoegen aan een winkelwagen, een cookie-melding of het uitvoeren van een zoekopdracht onmogelijk. Ze zijn onmisbaar voor de kernfunctionaliteit van de site.

Deze scripts moeten in de head van de pagina worden geplaatst met het async- of defer-attribuut.

<script defer src="critical.js"></script>
<script async src="critical.js"></script>

Aanbevolen werkwijzen:

  • Houd scripts zoals deze tot een minimum beperkt en combineer deze functionaliteit niet met andere, minder kritieke functionaliteit.
  • Laad deze scripts vroeg met async of defer, afhankelijk van hun dependencies.
  • Gebruik Real User Monitoring (RUM) tools, zoals Coredash, om bottlenecks in de uitvoering te identificeren en ervoor te zorgen dat hun prestaties aansluiten bij de behoeften van de gebruiker.

3. Important Scripts

Hoewel ze niet direct verbonden zijn met de bruikbaarheid van de site, ondersteunen belangrijke scripts belangrijke bedrijfsfuncties. Analytics-scripts bieden bijvoorbeeld essentiële data, maar hoeven niet te laden vóór belangrijkere visuele elementen. Vanzelfsprekend kan het onderscheid tussen kritieke en belangrijke scripts een punt van discussie zijn, dus zorg ervoor dat je met alle stakeholders praat voordat je deze prioriteit instelt!

Er zijn 3 manieren om de scriptprioriteit voor dit soort scripts te verlagen.

<html>
<head>
<!-- method 1: low fetchpriority -->
<script fetchpriority="low" defer src="important.js"></script>

<!-- method 2: inject after DOMContentLoaded -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    var script = document.createElement('script');
    script.src = 'important.js';
    document.body.appendChild(script);
  });
</script>
</head>
<body>

<!-- method 3: place at the bottom of the page -->
<script defer src="important.js"></script>
</body>
</html>

1. Lage fetchpriority. 

Het instellen van de fetchpriority verlaagt de relatieve prioriteit van het script. Andere deferred of asynced scripts worden waarschijnlijk in de wachtrij geplaatst met een hoge prioriteit, terwijl de scripts met fetchpriority="low" met een lage prioriteit in de wachtrij komen. Afhankelijk van je pagina (of je rendering path) kan dit genoeg zijn om andere resources zoals je Largest Contentful Paint-afbeelding en belangrijke lettertypen voorrang te geven. 

2: Injecteren na DOMContentLoaded

Door het script na het DOMContentLoaded-event te injecteren, zorg je ervoor dat het script direct na het volledig parsen van de HTML begint met downloaden. Dit geeft ontdekbare resources, zoals afbeeldingen en lettertypen, voorrang. Deze methode biedt een balans: het script begint vroeg genoeg met laden om vertragingen in functionaliteit te voorkomen, maar concurreert niet met vroege resources die cruciaal zijn voor de initiële paginarendering.

3: Plaatsen onderaan de pagina

Deze klassieke techniek stelt het laden van het script uit totdat de browser het hele document heeft verwerkt en bereikt ongeveer hetzelfde resultaat als techniek 2. Het enige verschil is dat techniek 2 de preload scanner van je browser overslaat, terwijl deze techniek dat niet doet. De preload scanner is een lichtgewicht, snelle scanner die je browser gebruikt om snel kritieke resources te identificeren en in de wachtrij te plaatsen. Het overslaan van de preload scanner kan een goed idee zijn als er een mogelijkheid is van lazy loaded afbeeldingen in de viewport, terwijl het gebruik van de preload scanner het laden van dit script zal versnellen.

4. Nice-to-Have Scripts

Deze scripts verbeteren de user experience maar zijn niet vereist om de site te laten functioneren. Voorbeelden zijn chat widgets, pop-ups voor klantenfeedback of optionele animaties. Hoewel ze nuttig zijn, mogen ze de primaire user experience niet verstoren.

Deze scripts zijn een ideale kandidaat om te laden met een patroon genaamd 'lazy on load'. Dit betekent wachten op het load-event van de pagina en dan, tijdens idle time, het script injecteren. Wachten op het load-event zorgt ervoor dat het script niet concurreert om bandbreedte en CPU met belangrijkere vroege resources. Wachten op een idle moment zorgt ervoor dat de browser niet bezig is met belangrijkere taken zoals user input.

Hier is een werkend voorbeeld:

window.addEventListener("load", () => {
  window.requestIdleCallback(() => {
    const script = document.createElement("script");
    script.src = "/path/to/script.js";
    document.head.appendChild(script);
  });
});

Aanbevolen werkwijzen:

  • Laad deze scripts met lazy-loading nadat de pagina is geladen en wacht op een idle moment.
  • Begrijp dat scripts die met dit patroon worden geladen, niet gegarandeerd snel laden.

5. Future Scripts

Future scripts zijn scripts die pas nodig zijn als aan specifieke voorwaarden is voldaan. Een multi-stage check-out script wordt bijvoorbeeld pas relevant nadat een gebruiker items aan zijn winkelwagen heeft toegevoegd. Deze scripts kunnen vaak wachten tot veel later in de journey van de gebruiker.

Kijk naar dit voorbeeld. Het gebruikt de IntersectionObserver om de JS-logica die nodig is voor het aanmeldscript pas te laden wanneer het formulier in de zichtbare viewport is.

<!DOCTYPE html>
<html>
  <head>
    <script>
      document.addEventListener("DOMContentLoaded", function () {
        const form = document.querySelector("form");
        const observer = new IntersectionObserver(function (entries) {
          entries.forEach((entry) => {
            if (entry.isIntersecting) {
              const script = document.createElement("script");
              script.src = "/sign-up.js";
              document.head.appendChild(script);
              observer.unobserve(form);
            }
          });
        });
        observer.observe(form);
      });
    </script>
  </head>
  <body>
    <form action="/sign-up" method="post">
      <label for="email">Email:</label>
      <input type="email" id="email" name="email" required />
      <button type="submit">Sign Up</button>
    </form>
  </body>
</html>

Aanbevolen werkwijzen:

  • Laad deze scripts on demand, getriggerd door gebruikersacties.
  • Gebruik code-splitting-technieken om alleen de delen te leveren die bij elke stap nodig zijn.
  • Injecteer ze dynamisch alleen wanneer dat nodig is, bijvoorbeeld wanneer een gebruiker naar een specifieke sectie scrolt.

Need your site lightning fast?

Join 500+ sites that now load faster and excel in Core Web Vitals.

Let's make it happen >>

  • Fast on 1 or 2 sprints.
  • 17+ years experience & over 500 fast sites
  • Get fast and stay fast!
JavaScript-prioriteit optimaliseren voor snellere laadtijdenCore Web Vitals JavaScript-prioriteit optimaliseren voor snellere laadtijden