Chunkování není dělení kódu, je to sázka na chování uživatele
Bundler nemá jak rozhodnout správně. Má dva cíle, které si odporují: stáhnout co nejmíň kódu a udělat co nejmíň requestů. Každé sloučení chunků posune jeden na úkor druhého. Turbopack ten spor neřeší, jen na něj sází s odhadem pravděpodobnosti.
Krajní řešení to ukážou nejrychleji. Jeden chunk pro celou appku znamená, že po prvním načtení je všechno v cache a navigace je okamžitá — a taky že stránka bez JavaScriptu stáhne JavaScript všech ostatních stránek. Jeden chunk na modul nikdy nepošle nic navíc, ale vyrobí stovky requestů. A hůř: komprese se láme na hranici souboru. Gzip hledá opakující se vzory jen uvnitř jednoho souboru, takže tisíc malých chunků se komprimuje výrazně hůř než jeden velký se stejným obsahem.
Chunk group posouvá cenu na druhou stránku
Turbopack zavádí mezistupeň, který ten spor zúží. Chunk group je množina chunků, které se načítají společně — třeba všechny chunky potřebné pro /home. Slučovat se smí jen uvnitř jedné skupiny.
To omezení je chytřejší, než vypadá. Když sloučíš dva chunky, které se na dané stránce načítají tak jako tak, na té stránce jsi nemohl nic zkazit. Ušetřil jsi jeden request a nestáhl ani bajt navíc. Cena sloučení se nikdy neprojeví na první stránce.
Projeví se až při navigaci. Vezmi A jako chunk s patičkou, kterou používá každá stránka, a B jako přehrávač videa, který je jen na homepage. Obojí je ve skupině homepage, takže je Turbopack smí sloučit. Uživatel načte homepage, dostane A+B v jednom souboru, a pak jde na stránku s podmínkami. Ta potřebuje A, ale sloučený soubor je pro ni k ničemu, takže si prohlížeč stáhne A ještě jednou.
Projdi si všechny dvojice a vyjde z toho jediný případ, kdy se sloučení při navigaci vyplatí: obě stránky potřebují oba chunky. Všechny ostatní kombinace stojí jedno stažení navíc, nebo jsou neutrální.
Celý algoritmus stojí na jednom odhadu
Když se tedy sloučení vyplatí na jednostránkové návštěvě a skoro nikdy na dvoustránkové, rozhodnutí závisí na tom, kolik je kterých. Turbopack předpokládá, že dvě třetiny sessions končí na první stránce.
To číslo není naměřené na tvém webu. Je to výchozí odhad napříč weby. Dokumentační web s hlubokou navigací a landing page s vysokým bounce rate si zaslouží opačné nastavení, a rozdíl není kosmetický — na nextjs.org vyšlo maximální slučování o 10 % víc staženého kódu, zato o dvě třetiny míň requestů než výchozí nastavení.
Next.js 16.3 proto ten odhad otevírá. firstPageLoadPriority posouvá váhu mezi prvním načtením a navigací a rozumná výchozí hodnota je tvůj skutečný bounce rate. priorityRoutes označí trasy, kde se cení první načtení. clusters popíše skupiny tras, které lidé navštěvují společně.
Co build time vědět nemůže
Zbývá díra, kterou žádné vážení nezaplní. Rozhodnutí o sloučení padá při buildu, tedy dřív, než kdokoli přišel. V tu chvíli není jak zjistit, co už prohlížeč v cache má.
Runtime to ví. experimental.turbopackChunking.generateComponentChunks proto nechá Turbopack vygenerovat vedle sloučených chunků i jejich nesloučené verze a při requestu se vybere levnější varianta — buď sloučený soubor, nebo jen ty kusy, které chybí. Funguje to i obráceně: kdo už má A+B, nestahuje A znovu.
Tohle je ten podstatný posun. Rozhodnutí se přesunulo z buildu na request, protože teprve tam existuje informace, na které záleží. Cena je duplicitní artefakty na disku.
Co si z toho odnést
Když příště uvidíš v network tabu dvacet chunků a budeš je chtít uklidit, ptej se, kterou z těch dvou os zhoršuješ. Míň requestů znamená míň znovupoužitelné soubory a naopak. Nastavení bez znalosti provozu je jen jiný odhad.
Vyplatí se ten odhad nahradit vlastním číslem. Bounce rate a nejčastější cesty webem máš v analytice a firstPageLoadPriority s clusters z nich udělají vstup algoritmu. Je to jedna z mála optimalizací, kde produkční data přímo mění výstup buildu.
A obecněji: kdykoli tvůj build dělá rozhodnutí o něčem, co závisí na stavu klienta, dělá ho naslepo. Buď mu ten stav dodej jako odhad, nebo rozhodnutí odsuň do runtimu.