eduardweb.
PerformanceIntermediar#performance#react#vite#javascript#webpack

Dynamic import fără filtrare: cum să nu-ți încetinești aplicația prin bundle splitting excesiv

De Mihai Popescu, 25 iun. 2026 · 21 vizualizări · 3 like-uri

Postat 25 iun. 2026
javascript
// Exemplu de grupare a chunk-urilor mici în Vite pentru a evita fragmentarea excesivă
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks(id) {
          // Grupăm toate bibliotecile de grafice într-un singur chunk, în loc de fișiere separate
          if (id.includes('node_modules/chart.js') || id.includes('node_modules/d3')) {
            return 'charts-bundle';
          }
          // Grupăm componentele secundare de UI într-un chunk comun destul de mare ca să merite request-ul
          if (id.includes('src/components/heavy-editors/')) {
            return 'editors-bundle';
          }
        }
      }
    }
  }
});

Toată lumea promovează dynamic import-ul ca pe Sfântul Graal al performanței în React sau Vue. Dai un React.lazy() sau un import() dinamic și, gata, ai rezolvat performanța. În realitate, dacă abuzezi de chestia asta, s-ar putea să livrezi o experiență mult mai proastă pentru utilizatorii tăi.

Am pățit asta acum doi ani la un proiect cu peste 15k utilizatori activi pe zi. Din dorința de a scoate un scor de 100 în Lighthouse, un coleg a pus dynamic import pe aproape fiecare componentă mai mare de 5KB. Pe hârtie, bundle-ul inițial a scăzut cu 40%. În realitate, pe conexiuni 3G sau 4G instabile, utilizatorii așteptau secunde bune la fiecare click pe un tab sau meniu doar pentru că browserul trebuia să facă fetch la un chunk minuscul de 3KB.

Costul ascuns al rețelei (Waterfall-ul ucigaș)

Chiar și în era HTTP/2 sau HTTP/3, unde multiplexarea rezolvă o parte din probleme, un request HTTP nu este gratis. Fiecare request nou aduce după sine un overhead de latență: DNS lookup (dacă e pe alt domeniu), TCP handshake, TLS negotiation și, cel mai important, RTT (Round Trip Time).

Dacă ai un chunk de 5KB gzipped, timpul fizic de descărcare a acelor octeți e neglijabil (sub 2ms). Însă timpul necesar pentru ca browserul să ceară fișierul și să primească primul octet (TTFB) poate fi de 100-200ms pe o rețea mobilă medie. Dacă acel chunk importat dinamic mai cere la rândul lui un alt chunk (cascade de importuri), ai generat un waterfall care distruge complet interactivitatea.

Cum calculezi break-even-ul (Pragul de rentabilitate)

Ca regulă generală pe care am aplicat-o în ultimele proiecte, pragul de rentabilitate (break-even) pentru un dynamic import depinde de mărimea codului și de frecvența de utilizare a acelei funcționalități.

Matematica simplă arată cam așa:

  • Sub 15KB (gzipped): Nu merită aproape niciodată să faci dynamic import. Costul latenței de rețea depășește cu mult beneficiul salvării a 15KB din bundle-ul principal. Mai bine trimiți acești KB în bundle-ul principal, unde browserul îi descarcă oricum într-un mod optimizat la încărcarea paginii.
  • Între 15KB și 50KB: Merită doar dacă funcționalitatea este folosită de mai puțin de 30% dintre utilizatori (de exemplu, un panou de admin, un editor de text complex accesibil doar la click, sau setări avansate).
  • Peste 50KB: Aici dynamic import-ul devine extrem de eficient. Biblioteci mari precum Chart.js, PDF generators sau date-fns cu toate locale-urile trebuie neapărat scoase din bundle-ul principal.

Trade-off-ul sincer

Dynamic import-ul oferă o încărcare inițială mai rapidă (FCP mai bun), dar penalizează interacțiunea ulterioară (timp de răspuns mai mare la click). Practic, muți frustrarea utilizatorului de la secunda 0 la secunda 10, când încearcă să folosească aplicația.

Pentru a evita fragmentarea excesivă în Vite sau Webpack, eu folosesc o strategie de group-ing pentru chunk-urile mici în fișierul de configurare. În loc să las bundler-ul să genereze 50 de fișiere mici, le grupez în câteva chunk-uri logice mai mari.

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

Doar membrii comunității pot lăsa comentarii.