Hvordan du stopper med at gætte dig til performance i Chrome DevTools

Hvordan du stopper med at gætte dig til performance i Chrome DevTools

De fleste tror en langsom hjemmeside handler om “for meget JavaScript”. Det passer ikke. Jeg har set sider, der føles som sirup, fordi ét billede var gigantisk, eller fordi en font blev hentet på den værst tænkelige måde.

Det er sjældent én magisk årsag. Det er et par små ting, der tilsammen gør oplevelsen træls. Og her er pointen: hvis du ikke har en fast måde at undersøge performance på, så ender du med at gætte og tweake tilfældigt.

Jeg vil vise dig, hvordan du bruger Chrome DevTools som et målebånd, ikke som et kaos-værktøj med 30 faner. Vi bygger en lille, gentagelig proces, du kan køre på alle dine projekter.

Før du åbner DevTools: hvad er det egentlig, der føles langsomt?

Inden du kaster dig over fanen “Performance” og får stress af alle farverne, skal du være lidt kedelig: definer problemet.

Stil to konkrete spørgsmål:

  • Hvornår føles det langsomt? Ved første load, ved klik, ved scroll?
  • Hvad føles langsomt? At se noget på siden, at kunne klikke, at noget reagerer?

Det kobler vi til nogle Web Vitals-begreber:

  • TTFB (Time To First Byte): hvor lang tid går der, før serveren overhovedet svarer.
  • LCP (Largest Contentful Paint): hvor lang tid går der, før den største synlige ting er tegnet.
  • INP (Interaction to Next Paint): hvor lang tid det føles, før klik og input giver visuelt svar.

Du behøver ikke kunne alle definitioner udenad. Brug dem bare som pejlemærker:

  • Høj TTFB → mistanke om server/proxy/database/cache.
  • Dårlig LCP → ofte billeder, fonts, CSS eller JavaScript der blokerer rendering.
  • Dårlig INP → ofte tung JavaScript eller layoutarbejde ved interaktion.

Hvis du vil have en blød intro til vitals, har jeg også skrevet om det i Core Web Vitals uden panik. Her går vi mere direkte efter værktøjerne.

Lav et fast test-scenarie

Vælg ét scenarie, du vil måle på, fx:

  • Åbn forsiden i inkognito, uden cache.
  • Åbn produktsiden og klik på “Læg i kurv”.
  • Åbn dashboardet efter login.

Det scenarie bruger du hver gang, du tester, både før og efter ændringer. Så har du i det mindste én stabil reference.

Trin 1 – Network: er det serveren, assets eller tredjeparts-ting?

Nu skal vi have DevTools i spil.

Åbn siden, højreklik, vælg “Inspect”. Gå til fanen Network. Genindlæs siden med Ctrl+R eller Cmd+R.

Sådan læser du waterfall uden at blive skør

Når du har kørt en load, vil du se en lang liste af requests. Klik på selve HTML-filen øverst (ofte din / eller index route).

  • TTFB: Står som “Waiting (TTFB)” under “Timing”. Er den over ~500 ms på en simpel side, bør du løfte et øjenbryn.
  • Content Download: Samme sted. Har mest betydning, hvis du sender meget HTML eller store filer.
  • Initiator: I tabeloversigten kan du tilføje kolonnen “Initiator”. Den fortæller, hvad der fik en given request til at ske (HTML, JS-fil, noget andet).
  • Priority: Viser, hvad browseren forsøger at hente tidligt. Vigtigt for billeder og fonts over folden.

Prøv nu at sortere efter “Time” og kig på de værste syndere i bunden.

Seks klassiske syndere i Network-fanen

Der er et par ting, jeg ser igen og igen, når en side føles tung. Du kan nærmest lave en lille bingo-plade ud af dem.

1. Gigantiske billeder

Tjek kolonnen “Size”. Finder du billeder på 1-5 MB, har du allerede en gevinst liggende.

  • Er billedet større i pixels end det vises på skærmen?
  • Bruger du et tungt format (PNG hvor JPEG eller WebP ville være fint)?

Første fix at prøve:

  • Resize billedet til max den størrelse, det vises i layoutet.
  • Gem som WebP eller AVIF, hvis browser-support passer til dit projekt.
  • Brug <img loading="lazy"> til billeder, der ikke er over folden.

2. Webfonts der blokerer tekst

Fonts dukker ofte op som 50-200 KB filer, nogle gange flere.

Hvis du ser font-requests, der først bliver hentet sent, og din tekst er usynlig eller hopper, er det en klar kandidat.

Første fix at prøve:

  • Brug font-display: swap; i din @font-face, så system-font vises, indtil webfonten er klar.
  • Drop varianter du alligevel ikke bruger (fx 100, 200, 900 vægte).

3. Bundling der blev lidt for vild

Kig efter store JS-filer: typisk app.js, main.[hash].js eller lignende.

Hvis du ser én 1 MB+ bundle, er det værd at overveje, om du kan splitte.

Første fix at prøve:

  • Aktiver code splitting i dit build-værktøj (Vite, Webpack, osv.).
  • Lazy load ruter eller tunge komponenter, som brugeren ikke altid rammer.

4. Manglende cache

Klik på en JS- eller CSS-fil, se fanen “Headers” og kig under “Response Headers” efter cache-control.

Hvis alt har no-store eller meget lav max-age, tvinger du brugeren til at hente det hele hver gang.

Første fix at prøve:

  • Sæt lang cache-tid (fx 1 år) på fingerprintede filer (main.123abc.js osv.).
  • Brug kortere cache på HTML (ofte hvor du styrer opdateringer).

5. Third-party scripts der tager over

Analytics, chat-widgets, A/B-testing osv. dukker ofte op i Network med domæner du ikke selv ejer.

Hvis en tredjeparts-request står og hænger, kan den blokere andre ting eller lægge pres på main thread.

Første fix at prøve:

  • Fjern scripts, du ikke bruger aktivt.
  • Load dem sent (defer eller via tag manager, der kun kører på visse sider).

6. Redirects og unødige hop

Hold øje med statuskoder: 301, 302. Hvis du ser en kæde som //home/da/home, har du spildtid lige ved start.

Første fix at prøve: peg brugeren direkte på den endelige URL, både i links og i serverkonfiguration.

Trin 2 – Performance-fanen: long tasks og main thread der brænder

Network fortæller dig, hvad der bliver hentet og hvor lang tid det tager. Men det siger ikke noget om, hvad browseren laver på selve maskinen.

Det er her Performance-fanen i DevTools kommer i spil.

Optag en performance-profil

Gør sådan her:

  1. Åbn DevTools, vælg fanen Performance.
  2. Sæt evt. “Network” til “Fast 3G” eller “Slow 4G” i toppen, hvis du vil simulere dårligere forbindelse.
  3. Tryk på runde “Record”-knap.
  4. Genindlæs siden (eller udfør det scenarie du vil teste, fx klik på en knap).
  5. Vent et par sekunder efter, tingene føles færdige, og stop så optagelsen.

Nu får du en tidslinje med en masse farvede blokke. Det er her, mange står af. Lad os pille det lidt fra hinanden.

Long tasks: de røde streger du ikke vil se

I toppen af tidslinjen vil du ofte se små røde markeringer. Holder du musen over, står der “Long task”.

En “long task” er, meget forsimplet, et stykke arbejde på main thread, der tager mere end 50 ms. Det betyder, at browseren i den periode har svært ved at reagere på input, animations-frames osv.

Klik på en long task. Nede i “Bottom-Up” eller “Call Tree” kan du se, hvilken kode der bidrager.

// Typisk mønster der giver long tasks
button.addEventListener('click', () => {
  // Tung beregning direkte ved klik
  for (let i = 0; i < 10_000_000; i++) {
    // ... et eller andet
  }

  // DOM-manipulation i en stor klump
  renderBigList();
});

Typiske årsager til long tasks:

  • Store loops, der kører synkront.
  • Mange DOM-ændringer på én gang.
  • Tung JSON-parsing eller data-transformation i browseren.
  • Tredjeparts-scripts, der laver meget arbejde i baggrunden.

Første fix at prøve:

  • Split lange loops op og brug requestAnimationFrame eller setTimeout i små bidder.
  • Batch DOM-opdateringer i stedet for at opdatere 1000 gange i træk.
  • Flyt tung behandling til en Web Worker, hvis det giver mening.

Scripting vs rendering vs painting

I toppen af Performance-tidslinjen har du små lagkage-lignende felter med farver:

  • Gul → Scripting (JavaScript).
  • Lilla/blå → Rendering/layout.
  • Grøn → Painting/compositing.

Kig på mønstret:

  • Meget gul samlet i klumper → din JavaScript er tung.
  • Store lilla blokke, særligt efter DOM-ændringer → layout bliver genberegnet igen og igen.
  • Store grønne blokke → tegning af mange elementer, ofte ved animationer eller scroll.

Hvis du fx ser en kæmpe gul blok, hver gang du skriver i et input, er det en indikator på, at du måske laver for meget arbejde på input-eventet.

// Klassisk INP-dræber
input.addEventListener('input', handleSearch);

function handleSearch(e) {
  const value = e.target.value;
  // Triggerer API-kald og fuld re-render for hvert tastetryk
  fetch(`/search?q=${value}`)
    .then(res => res.json())
    .then(renderResults);
}

Første fix at prøve: brug debounce:

function debounce(fn, delay) {
  let id;
  return (...args) => {
    clearTimeout(id);
    id = setTimeout(() => fn(...args), delay);
  };
}

input.addEventListener('input', debounce(handleSearch, 250));

Nu får du langt færre lange scripting-perioder ved input, og INP har en chance.

Trin 3 – Memory: hurtig sanity check for lækager

Hvis din side bliver værre og værre, jo længere den er åben, kan du have en memory leak eller lignende.

Chrome har en Memory-fane, som kan meget mere, end vi får brug for her. Vi bruger den bare til en simpel test:

  1. Åbn fanen Performance.
  2. Aktiver “Memory”-checkbox i toppen.
  3. Optag mens du bruger siden som normalt i 20-30 sekunder.

Hvis memory-grafen bare kravler støt opad uden at falde, selvom du går frem og tilbage mellem visninger, kan du have komponenter, event listeners eller data, der aldrig bliver ryddet op.

Typiske kilder:

  • Event listeners der bliver tilføjet ved hver render, men aldrig fjernet.
  • Globale arrays/objekter, du bare tilføjer til, men aldrig rydder.
  • SPA-ruter, der holder referencer til gamle views.

Her giver det mening at dykke mere ned i Chromes egen memory-dokumentation, men ofte kan du komme ret langt bare ved at rydde op i event listeners.

Trin 4 – Verificér dine fixes med samme setup

Performance-arbejde uden måling før/efter er basically religion. Det føles rigtigt, men du ved ikke, om det hjalp.

Gør det her til en vane:

  1. Notér et par tal før du ændrer noget: TTFB på HTML, LCP fra Performance, evt. INP via Web Vitals extension eller Lighthouse.
  2. Lav ét fokuseret fix ad gangen (fx optimer hero-billedet).
  3. Deploy/byg igen.
  4. Kør samme scenarie, samme Network-throttling, samme enhedstype.
  5. Sammenlign tal.

Hvis du fx går fra LCP 3,2 s til 1,6 s efter at have gjort hero-billedet mindre og flyttet det direkte ind i HTML-markup, har du en ret klar årsagssammenhæng. Det er også noget, du kan vise i en ansøgning eller til en kollega, i stedet for “jeg tror den er hurtigere nu”.

Hvis du vil kombinere det her med Lighthouse, så brug Lighthouse til overblik og DevTools til selve fejlfindingen. Jeg har det lidt sådan, at Lighthouse er som din karakter i skolen, mens DevTools er dine rettede opgaver.

Der ligger i øvrigt en artikel om netop den kombination i “Core Web Vitals uden panik” og en mere generel fejlfindingstilgang i “Jeg stoler mere på mine logs end på min hukommelse“.

Mini-tjekliste: symptom → årsag → første fix

Her er den lille mentale matrix, jeg selv bruger, når nogen siger “siden er langsom” uden flere detaljer.

1. Siden tager lang tid om overhovedet at begynde at loade

Mest sandsynlige årsag: høj TTFB, serveren svarer langsomt.

Tjek i DevTools:

  • Network → klik på HTML-request → “Waiting (TTFB)”.

Første fix at prøve:

  • Tænd caching af HTML, hvis indholdet ikke skifter for hvert request.
  • Hvis du kører SSR eller et tungt API-kald under render, så cache det resultat.
  • Mål database-queries på serveren, hvis du har adgang til logs.

2. Indholdet dukker sent op, men når det først er der, føles siden ok

Mest sandsynlige årsag: tung LCP, ofte billede/font/CSS/JS der blokerer.

Tjek i DevTools:

  • Performance → se LCP-markeringslinjen.
  • Kig på Network for store filer, der loader før LCP.

Første fix at prøve:

  • Optimér størrelsen på det billede der er LCP-elementet.
  • Sørg for at CSS til layout er kritisk og loader tidligt.
  • Flyt ikke-essentielt JS væk fra initial load (defer/lazy).

3. Siden hakker ved scroll eller animationer

Mest sandsynlige årsag: for meget layout/paint-arbejde per frame.

Tjek i DevTools:

  • Performance → optag mens du scroller.
  • Kig efter store lilla/grønne blokke, der flugter med scroll.

Første fix at prøve:

  • Undgå at ændre layout-egenskaber i scroll-handlers (top, left, width, height).
  • Brug transform og opacity til animationer.
  • Reducer antal elementer i DOM, hvis du renderer lister på flere tusinde elementer.

4. Klik og input føles trægt

Mest sandsynlige årsag: long tasks på main thread under interaktion.

Tjek i DevTools:

  • Performance → optag mens du klikker eller skriver.
  • Kig efter long tasks (røde streger) lige efter interaktion.

Første fix at prøve:

  • Debounce input-håndtering.
  • Flyt tung logik væk fra selve klik-øjeblikket.
  • Prøv at give hurtig visuel feedback først (fx disabled-knap, spinner), og lav derefter det tunge arbejde.

5. Siden bliver langsommere, jo længere den er åben

Mest sandsynlige årsag: memory leak eller tiltagende arbejde, der aldrig ryddes op.

Tjek i DevTools:

  • Performance med “Memory” aktiveret → optag 30-60 sekunder.
  • Kig om memory-grafen over tid bare vokser og vokser.

Første fix at prøve:

  • Ryd op i event listeners ved unmount/destroy.
  • Undgå globale arrays, der vokser uden begrænsning.
  • Tjek at dit SPA-router-framework faktisk fjerner gamle views.

Når det faktisk er serveren der er langsom

Nogle gange kan du optimere billeder og JavaScript lige så tosset du vil. Hvis TTFB er 2 sekunder, når du sidder på en hurtig forbindelse, har du et serverproblem.

Hvad du måler i DevTools

Igen: Network → HTML-request → “Timing”.

  • Blocked/Queueing: tid i kø i browseren (fx for mange parallelle requests).
  • DNS Lookup: kan være lidt højt første gang, men bør ikke dominere.
  • Initial connection: TCP/SSL handshake, typisk ok.
  • Waiting (TTFB): det er her serveren rent faktisk laver arbejde.

Hvis “Waiting (TTFB)” er klart den dominerende del, er det sjældent noget, du fixer i frontend.

Næste skridt på server-siden

Hvis du har backend-adgang:

  • Log runtime for vigtige endpoints (fx hvor lang tid database-queries tager).
  • Test samme endpoint direkte via et REST-værktøj (fx Postman eller curl), uden hele siten.
  • Overvej at cache HTML eller API-respons til de mest besøgte sider.

Hvis du kører på en host som Vercel, Netlify osv., så tjek deres logs og performance-værktøjer. Kombinér det med det du ser i DevTools, så har du både “føles langsom” og “er langsom” dækket.

Afslutning: fra kaos-faner til en fast rutine

Jeg åbnede selv Chrome DevTools Network første gang, fordi en side tog en evighed at loade, og jeg troede, det var “React der var langsomt”. Det viste sig at være ét billede på 7 MB fra en kundes iPhone, der blev vist som logo i headeren. React var uskyldig.

Pointen er: du behøver ikke kunne alle faner og grafer udenad. Hvis du bare holder dig til en simpel rytme – definér problemet, start i Network, kig efter long tasks i Performance, lav ét fix ad gangen og mål før/efter – så er du allerede foran mange, der bare kaster endnu et framework efter problemet.

Næste gang en kollega siger “siden føles tung”, kan du roligt trække DevTools frem, sætte din proces i gang og være den stille person i hjørnet, der faktisk finder årsagen i stedet for at gætte.

Åbn fanen Performance, tryk Record og genindlæs siden for at få et load-trace. Efter stop kan du se LCP-markøren i Timings/Experience-sektionen; for INP optag en repræsentativ interaktion og tjek 'Interactions' eller Long Tasks på Main-thread for forsinkede paints. Brug også Lighthouse-rapporten for en hurtig opsummering af vitals.
Optag et trace i Performance under den handling, der føles langsom, og kig i Main-thread flame chart efter lange opgaver (long tasks >50 ms). Klik på en long task for at se stack trace og filnavne, og brug Initiator-kolonnen i Network til at koble requests til den pågældende kode.
Brug Device Toolbar til at simulere en mobil enhed, vælg netværks-throttling i Network-menuen (fx Slow 3G) og slå CPU-throttling til i Performance-panelet (fx 4x slowdown). Husk altid at køre tests både med og uden throttling og slå cache fra for konsistente resultater.
Start med Lighthouse CLI eller Lighthouse CI til automatisk rapportering i din build pipeline, eller brug WebPageTest/Labsettings for detaljerede traces. Du kan også skrive en lille Puppeteer- eller Playwright-script, der henter trace-data og gemmer dem som artefakt for hver kørsel.

Lasse Falkenberg er typen, der begyndte at rode med HTML og CSS for at lave en simpel bandside – og opdagede, at det var langt sjovere at få knapperne til at virke end at stå på scenen. Siden har han kastet sig over alt fra små JavaScript-snippets til Python-scripts, der kan spare ham for kedeligt, manuelt arbejde i hverdagen.

Han har lært det meste ved at bygge ting, der lige præcis løser hans egne problemer: en lille webapp til at holde styr på brætspilsaftener, et script til at rydde op i rodede mapper, eller en enkel side til at dele noter med venner. Undervejs har han kæmpet sig gennem alle de klassiske fejl – semikolon, forkerte indrykninger og variabler, der hedder noget helt andet end man tror – og det er præcis den rejse, han deler på Coding Class.

På Coding Class skriver Lasse praktiske, jordnære guides, der tager udgangspunkt i små, konkrete opgaver: noget du kan se, teste og bygge videre på med det samme. Han elsker at bryde en opgave ned i små bidder, vise den fulde kode og forklare linje for linje, hvad der sker – inklusive de typiske bugs, du med stor sandsynlighed også støder på.

For Lasse handler kodning ikke om flotte titler eller store ord, men om følelsen af at få noget til at virke – og om at du som læser kan gå derfra med noget, du selv har bygget. Hvis du kan kende glæden ved at få en fejl til endelig at forsvinde, er du lige på bølgelængde med hans måde at lære fra sig på.

1 kommentar

comments user
Mia

Åh! Det forklarer mit sirup-site, et kæmpe billede var synderen 😅
Skal lave hjemmeside!

Send kommentar

You May Have Missed