Code splitting i lazy loading za brže web aplikacije

Code splitting i lazy loading smanjuju JavaScript, CSS i medije koje posjetilac mora preuzeti prije nego što stranica postane upotrebljiva. Prodavnica, dashboard ili marketing sajt mogu slati jedan ogroman bundle koji se parsira pri svakoj posjeti, ili mali prvi prikaz, a grafikon, mapu, editor i slike van ekrana učitati tek kada nekome zaista zatrebaju.

U CodeWizu pregledamo šta browser preuzima, dijelimo bundle po rutama i po funkcijama, odgađamo medije i tuđe skripte i u CI stavljamo budžet veličine, tako da sljedeća funkcija ne pojede dobitak u tišini. Rad pokriva Vite, webpack, Rollup, Next.js, Nuxt, obične PHP šablone i WordPress, uključujući prodavnice u kojima nekoliko plugina učitava skripte na svakom URL-u.

Usluga je za timove kojima Lighthouse ocjena padne svaki put kada se doda widget, za prodavnice čiji mobilni posjetioci odustanu tokom dugog bijelog ekrana i za proizvode koji kasne na klik iako server odgovara brzo. Kada su code splitting i lazy loading vezani za budžet i za podatke sa stvarnih uređaja, stranica ostaje brza kako proizvod raste.

Lazy: Osoba pakuje mali kartonski paket na svijetlom kancelarijskom stolu

Optimizacija bundlea: manje JavaScripta u prvom prikazu

Optimizacija bundlea znači pronaći šta je završilo u JavaScript fajlu i ukloniti, zamijeniti ili odgoditi sve što prvom prikazu ne treba. Bundler kao što su Vite, Rollup ili webpack prođe graf importa i izbaci jedan ili više fajlova. Tree-shaking odbacuje nekorištene exporte kada je kod pravi ESM i kada je oznaka sideEffects iskrena. Ne spašava vas od root importa paketa ikona, biblioteke za datume ili UI kita koji sve re-eksportuje iz jednog barrel fajla.

Šta težak bundle košta poslovanje

Vrijeme preuzimanja je tek prvi trošak. Browser taj JavaScript još mora parsirati i kompajlirati na main threadu, a na telefonu srednje klase taj posao odgađa prvi prikaz i prvi klik. Google u Core Web Vitalsu Largest Contentful Paint iznad 2.5 sekunde i Interaction to Next Paint iznad 200 milisekundi tretira kao loš rezultat. Oba se pogoršavaju kada stranica pri startu vuče stotine kilobajta koda koji se ne koristi.

Kako optimizujemo bundle

  • Vizuelna mapa bundlea uz rollup-plugin-visualizer, webpack-bundle-analyzer ili Next.js analizator, tako da su najteži moduli vidljivi prije nego što iko prepisuje komponentu.
  • Direktni importi umjesto barrel fajlova, i zamjena biblioteka manjima gdje API to dozvoljava: pomoćnik za datume veličine jednog jezika umjesto biblioteke za sve namjene, pojedinačne ikone umjesto cijelog seta.
  • Browserslist koji odgovara browserima koje zaista podržavate, tako da moderni posjetioci ne preuzimaju polyfille za engine koje više ne servirate.
  • Chunk po ruti za svaki važniji ekran, plus zajednički vendor chunk koji se mijenja samo kada se mijenjaju zavisnosti, pa se povratnicima čuva dugotrajni keš.
  • Provjera u CI-ju alatima size-limit, bundlesize ili Lighthouse budžetom. Pull request koji početni JavaScript gurne preko dogovorenog limita pada prije mergea.
PristupŠta se dijeliKada se učitavaNa šta paziti
Chunkovi rutaSvaka stranica ili layoutPri navigacijiPrazan razmak ako sljedeći prikaz nema placeholder
Import komponenteTežak widgetNa klik, u idle vremenu ili kada postane vidljivKašnjenje pri prvoj upotrebi tog widgeta
Vendor chunkZajedničke bibliotekeSa prvom stranicom kojoj trebajuJedna izmjena zavisnosti može poništiti veliki keširani fajl
Podjela CSS-aStilovi jedne ruteZajedno sa tom rutomBljesak nestilizovanog sadržaja ako nema kritičnog CSS-a
Odgođene tuđe skripteChat, tagovi, video playeriNakon pristanka ili interakcijeZadržite tagove koje poslovanje zaista mjeri

Operator dinamičkog import() je jezička mogućnost iza većine ovih podjela. Bundler svaki import pretvara u zaseban fajl; mi odlučujemo koji importi pripadaju prvom ekranu.

Lazy loading slika, videa i sadržaja van ekrana

Lazy loading odgađa posao koji posjetilac još ne vidi. Na hero slici pogoršava Largest Contentful Paint. Na svemu ispod prvog ekrana, na embedovima i na skriptama koje čekaju klik, to je jedno od najjeftinijih ubrzanja postojećeg sajta.

Slike, video i embedovi

  • LCP slika ide odmah: navedene širina i visina, moderan format kao AVIF ili WebP, responzivni srcset i fetchpriority="high", da je browser povuče prvu.
  • Slike ispod prvog ekrana koriste loading="lazy" i decoding="async", sa dimenzijama da se layout ne pomjeri kada stignu.
  • Video koristi preload="none" i poster sliku. Fajl od više megabajta ne počinje preuzimanje na stranici koju posjetilac možda nikad ne pusti.
  • YouTube, mape i drugi iframeovi ostaju statički poster do klika, a pravi iframe se montira tek tada. loading="lazy" pokriva embedove koji moraju biti prisutni ranije.
  • Dugi članci mogu koristiti content-visibility: auto na sekcijama daleko dole, pa browser preskače layout dok čitalac ne stigne.
<img src="hero.avif" alt="Product on a table" width="1600" height="900" fetchpriority="high"> <img src="gallery.avif" alt="Detail of the same product" width="800" height="600" loading="lazy" decoding="async">

Widgeti i tuđe skripte

Chat widgeti, tagovi za A/B testove, help desk i društveni embedovi često pojedu više vremena na main threadu nego sama stranica. Učitavamo ih nakon prve interakcije, nakon pristanka ili kada im se kontejner približi viewportu. Na WordPressu i WooCommerceu isto važi za plugine: skripte i stilovi idu samo na šablone koji ih koriste, a korpa, checkout i nalog zadržavaju asete koji im zaista trebaju. CDN i sloj keša i dalje su važni za HTML i slike oko tog JavaScripta — isto edge podešavanje koje opisujemo u vodiču o profesionalnoj Cloudflare konfiguraciji.

Izbliza prikazan JavaScript kod sa funkcijama i callbackovima na tamnom ekranu

Dinamički importi: kod stiže u trenutku korištenja

Dinamički import je poziv funkcije koji vraća promise i govori bundleru da izbaci zaseban chunk. Editor, mapa, traka za bogati tekst, admin grafikon i fajl jezika koji posjetilac nije izabrao ostaju van početnog preuzimanja. Prvi put kada zatrebaju, browser povuče taj jedan fajl.

document.querySelector('#open-chart').addEventListener('click', async () => { const { renderChart } = await import('./chart.js'); renderChart(document.querySelector('#chart'), window.reportData); });
  • React: lazy() i Suspense oko komponente koja ima default export, uz fallback koji rezerviše prostor da stranica ne poskoči.
  • Vue i Nuxt: defineAsyncComponent, ili Nuxtovi prefiksi za lazy komponente, za panele koji se otvaraju po potrebi.
  • Next.js: segmenti ruta već dijele App Router. next/dynamic pokriva widgete samo za klijent, poput mapa koje ne treba da rade tokom renderovanja na serveru.
  • Vite i webpack: dovoljan je običan import(). manualChunks drži React, Vue ili drugi stabilan runtime u vlastitom keširanom fajlu. Prefetch ide na jednu ili dvije rute koje će posjetilac vjerovatno otvoriti sljedeće, a ne na svaki chunk.

Imenujemo fallback, stanje greške i timeout. Chunk koji padne na nestabilnoj mobilnoj mreži treba ponuditi ponovni pokušaj, a skeleton učitavanja treba odgovarati visini widgeta.

Praćenje performansi za code splitting i lazy loading

Podjela koju niko ne mjeri vremenom se raspe. Nove zavisnosti, novi plugin i novi marketing tag dodaju težinu, a nazadovanje se u podacima sa stvarnih uređaja vidi tek sedmicama kasnije. Praćenje performansi zatvara taj krug: laboratorijska provjera na svakoj izmjeni i podaci sa stvarnih uređaja.

SignalCilj koji pratimoOdakle dolazi
JavaScript pri prvom učitavanjuIspod dogovorenog budžeta u kilobajtimaAnalizator bundlea i provjera veličine u CI-ju
Largest Contentful PaintIspod 2.5 sekunde u stvarnom saobraćajuweb-vitals, CrUX, Lighthouse
Interaction to Next PaintIspod 200 milisekundiweb-vitals i performance trace
Dugi zadaci na main threaduManje zadataka preko 50 milisekundi pri startuChrome performance panel
Keširanje chunkovaStabilan hash za kod koji se nije mijenjaoCDN logovi i Cloudflare analitika
  • Lighthouse CI radi na pull requestovima uz mobilni profil, pa je teža stranica vidljiva prije objave.
  • Biblioteka web-vitals šalje LCP, INP i CLS iz stvarnih sesija u analitički alat koji već koristite.
  • Podaci Chrome UX Reporta, kada domen ima dovoljno prometa, pokažu da li je laboratorijski dobitak preživio stvarne telefone i mreže.
  • Source mape idu u praćenje grešaka, a ne u javnost kao dodatna težina preuzimanja.
  • Kratka bilješka u repozitoriju pamti budžet i koje rute smiju biti teže, na primjer editor za prijavljenog korisnika.

Laboratorijske ocjene i podaci sa stvarnih uređaja odgovaraju na različita pitanja, i držimo oboje. Ista navika mjerenja vodi nas i ka rezultatu 100/100 za performanse stranice, uz keš ispred HTML-a kada stack to traži — uključujući Varnish ispred dinamičkog sajta. Googleova dokumentacija za Core Web Vitals je referenca za pragove iznad.

Podjela po rutama, zajednički chunkovi i granica koristi

Više chunkova samo po sebi ne znači brži sajt. Svaki fajl je zahtjev, a vodopad sitnih chunkova može pojesti vrijeme koje ste uštedjeli smanjenjem prvog. HTTP/2 i HTTP/3 čine mnogo malih fajlova jeftinijim nego na HTTP/1.1; i dalje nisu besplatni. Kod grupišemo onako kako se posjetioci zaista kreću kroz proizvod.

  • Jedan chunk po ruti ili layoutu, a ne jedan chunk po dugmetu.
  • Biblioteke koje koristi skoro svaka stranica žive u zajedničkom chunku sa stabilnim hashom.
  • Kod jednog admin ekrana učitava se samo unutar tog ekrana.
  • Kritični CSS prvog prikaza je ubačen u stranicu ili učitan odmah; ostatak može pratiti rutu.
  • Prefetch ide na hover ili u idle za sljedeću vjerovatnu stranicu, i to tek kada je trenutna stranica mirna.

Stranice renderovane na serveru i statičke stranice prate isto pravilo. HTML prvog ekrana treba već sadržavati sadržaj, a kasniji JavaScript treba da ga nadogradi.

Code splitting i lazy loading: česte greške

  • Lazy loading hero slike, što odgađa Largest Contentful Paint baš na elementu koji Google mjeri.
  • Import cijele biblioteke ikona ili komponenti iz korijena paketa, što poništava tree-shaking.
  • Poziv import() na svakoj maloj komponenti, pa čekanje na lanac zahtjeva već pri startu.
  • Slanje polyfilla i legacy bundleova modernim browserima jer je browserslist ostao na podrazumijevanoj vrijednosti.
  • Zaboravljen fallback fiksne visine, pa sadržaj poskoči kada stignu lazy slika ili chunk, a Cumulative Layout Shift strada.
  • Zeleni Lighthouse na desktopu uzet kao dokaz, dok podaci sa mobilnih uređaja i dalje pokazuju sporu interakciju.
  • WordPress plugini koji ispisuju skripte u footeru svakog šablona, uključujući stranice koje tu funkciju nikad ne koriste.

Od čega zavisi cijena rada na bundleu i učitavanju

Procjenu dajemo nakon što pogledamo produkcijski build, a ne odoka. Cijena prati oblik frontenda:

  • Jedna Vite ili Next.js aplikacija sa jasnom mapom ruta manji je posao od sajta složenog od nekoliko plugina i page buildera.
  • Broj šablona koji svaki učitava svoje skripte, posebno na WordPressu, WooCommerceu i sličnim stackovima.
  • Koliko tuđih tagova marketing mora zadržati, i koji od njih mogu čekati pristanak ili klik.
  • Da li su izmjene dizajna u opsegu, ili je rad ograničen na ponašanje učitavanja i izlaz builda.
  • Praćenje: jednokratni audit, ili budžet koji ostaje u CI-ju i izvještaj sa stvarnih uređaja koji ostaje uključen.
Brzinomjer i obrtomjer na upravljaču motocikla, pogled odozgo

Kako radimo

  1. Upoznavanje: mjerimo trenutni build, šablone i vitals sa stvarnih uređaja, i izlistamo module i tagove koji dominiraju prvim učitavanjem.
  2. Plan: dogovaramo budžet za početni JavaScript, koje rute se dijele, koje slike ostaju hitne i koje tuđe skripte čekaju.
  3. Izrada: mijenjamo importe, dodajemo dinamičke podjele, popravljamo markup slika i budžet bundlea vežemo za CI.
  4. Testiranje: uporedimo prije i poslije na mobilnom profilu i na stvarnom telefonu, uključujući sporu mrežu i chunk koji ne stigne.
  5. Puštanje: objavljujemo iza postojećeg sloja keša, pratimo LCP i INP sa stvarnih uređaja i držimo brzi rollback builda.
  6. Podrška: budžet ostaje na mjestu kako stižu nove funkcije, a chunkove preispitujemo kada ažuriranje zavisnosti pomjeri brojeve.

Zašto CodeWiz

  • Full-stack rad na performansama: podjela bundlea, markup slika, Varnish i Cloudflare su kod istog tima koji gradi aplikaciju.
  • Open-source alati: Vite, Rollup, webpack, Lighthouse i web-vitals, bez vlasničkog paketa za performanse.
  • Savjet prema stacku: WordPress prodavnica i Next.js proizvod dobijaju različite podjele, jer su im uska grla različita.
  • Tehnologija u službi poslovanja: sječemo bajtove koji odgađaju kupovinu ili prijavu, a bezazlen kod ostavljamo na miru.
  • Fer cijene: vrhunski razvoj bez vrhunskih cijena, iz Banje Luke, uz rad na daljinu sa klijentima iz regiona i svijeta. Više o CodeWizu.

Često postavljana pitanja

Da li code splitting poboljšava Core Web Vitals?

Poboljšava vitals na koje JavaScript i slike zaista utiču. Manje skripti u prvom prikazu obično pomaže LCP-u i INP-u, a tačne dimenzije slika čuvaju CLS. Dobitak se vidi kada je uklonjeni kod bio na kritičnoj putanji. Mjerimo prije i poslije, umjesto da obećavamo konkretnu ocjenu.

Koja je razlika između code splittinga i lazy loadinga?

Code splitting razbija build na manje fajlove. Lazy loading odlučuje kada se ti fajlovi, i kada slike ili embedovi, traže. Bundle se može podijeliti, a svi chunkovi i dalje preuzeti odmah. Korisna kombinacija je mali početni fajl i jasno pravilo za sve ostalo.

Da li lazy loading šteti SEO-u?

Lazy loading slika ispod prvog ekrana je uobičajen i očekivan. Problem nastaje kada se glavni sadržaj sakrije iza JavaScripta na koji pretraživač ili korisnik moraju čekati, ili kada se odgodi slika koja čini najveći paint. Prvi ekran ostaje u HTML-u, a lazy loading ide na ono što dolazi poslije.

Može li se ovo uraditi na postojećem WordPressu ili React sajtu?

Može. React, Vue, Next.js i Nuxt projekti obično traže izmjenu importa i budžet u CI-ju. WordPress i WooCommerce projekti obično traže pregled skripti po šablonu, popravku markupa slika i odgodu tuđih tagova. Prepisivanje je rijetko potrebno.

Koliko traje optimizacija bundlea?

Fokusiran prolaz kroz jednu aplikaciju — analizator, najveći krivci, lazy loading slika i budžet u CI-ju — često je kratak angažman. Veliki WordPress sajt sa više tema ili migracija design sistema sa mnogo ruta traje duže, uglavnom jer svaki šablon treba provjeriti. Procjenu dobijate nakon što vidimo produkcijski build.

Koji bundler preporučujete?

Za nove frontende aplikacija obično koristimo Vite. Postojeći webpack, Next.js ili Nuxt buildovi ostaju na svom alatu, osim kada je sam build problem. Pravila podjele važnija su od imena bundlera, i to ćemo reći kada bi migracija koštala više nego što vraća.

Spremni za manji prvi load?

Pošaljite nam produkcijski URL ili build, i pokazaćemo šta prvi prikaz preuzima i koje podjele vrijedi uraditi. Kontaktirajte nas da razgovaramo o projektu, ili pogledajte naše usluge za code splitting i lazy loading.