Налаштування нових контролів чанкінгу Turbopack у Next.js 16.3
Практичний огляд нової налаштування turbopackChunking у Next.js 16.3, де пояснюється, як maxChunkCountPerGroup та generateComponentChunks впливають на розмір пакету та кешування.
Було 4:40 у п’ятницю — саме той момент, коли ви закриваєте ноутбук на вихідні, — коли звичайна розгортка дашборд-додатку раптово перестала бути звичайною. Компіляція, яка зазвичай тривала 40 секунд, цього разу зайняла трохи більше трьох хвилин, а розмір пакету клієнта для основного маршруту збільшився майже на 300 кБ. Жодних маршрутів не торкалися, жодних нових залежностей не додавали, які могли б це пояснити. Єдиною зміною того тижня було незначне оновлення версії Next.js. Наступила ніч, проведена за переглядом вкладки „Мережа“, де чотири величезні файли JavaScript завантажувалися один за одним, тоді як раніше цю роботу виконувало десяток невеликих файлів.
Причиною не була помилка. Це просто логіка чанкінгу Turbopack функціонувала саме так, як було спроєктовано, просто не у спосіб, який відповідав очікуванням.
Чому проблема чанкінгу так і не була вирішена
Ця проблема існує вже близько десяти років, і інструменти для об’єднання файлів постійно намагаються її вирішити. У Webpack існує плагін SplitChunksPlugin, який надає параметри на кшталт minSize, maxSize та cacheGroups, але майже ніхто насправді не розуміє їх сутності з основ, оскільки вони переважно були скопійовані з якоїсь теми на Stack Overflow, що датується 2019 роком. Основний компроміс залишається незмінним: об’єднання файлів у меншу кількість більших частин зменшує кількість HTTP-запитів, але погіршує функціонування кешу, адже одна зміна у CSS може зробити недійсним весь пакет розміром 500 кБ. Розділення на багато менших частин покращує деталізацію кешу, але збільшує кількість операцій, що швидко стає помітним при повільному з’єднанні.
Turbopack успадкував цю саму проблему балансування, і до нещодавнього часу він сильно схилявся до консолідації. Це розумний вибір для сесії з однією сторінкою, де більшість ресурсів все одно завантажуються лише один раз. Однак це стає проблемою, якщо користувачі постійно навігують у великому додатку, оскільки раніше збережений у кеші код постійно перезавантажується разом із будь-яким новим кодом, який опиняється поруч з ним.
Що змінилося у версії 16.3
У Next.js 16.3 поведінка об’єднання більше не прихована за однією непрозорою евристикою — тепер вона прямо налаштовується через конфігурацію. Був доданий новий блок turbopackChunking, а двома найважливішими параметрами є maxChunkCountPerGroup та maxMergeChunkSize.
// next.config.js
module.exports = {
experimental: {
turbopackChunking: {
maxChunkCountPerGroup: 40, // default
maxMergeChunkSize: 200_000, // bytes, default
},
},
};
Зниження значення maxChunkCountPerGroup спонукає Turbopack до більш агресивного об’єднання коду: менше запитів під час завантаження сторінки, але слабкіша збереженість кешу під час навігації. Підвищення цього параметра створює менші, але більш численні частини коду, які залишаються недоторканими навіть тоді, коли змінюються не пов’язані частини додатку. maxMergeChunkSize встановлює максимальний розмір об’єднаної частини коду, після якого Turbopack припиняє додавати до неї новий код, що є корисним у випадках, коли одна велика залежність від постачальника схильна поглинати все навколо себе.
Раніше жоден з цих параметрів не існував. Ви просто отримували те, що генерувалося за замовчуванням, та пристосовувалися до цього.
Опція часткового розділення компонентів
Ще одним значущим доповненням є generateComponentChunks, який розширює можливості Turbopack щодо часткового розділення коду до окремих компонентів, а не обмежуючись лише межами маршрутів.
module.exports = {
experimental: {
turbopackChunking: {
generateComponentChunks: true,
},
},
};
Увімкнення цього режиму змушує Turbopack виводити як об’єднані, так і необ’єднані версії частин коду, що дозволяє системі під час завантаження вирішити, яку версію отримати, ґрунтуючись на тому, що вже зберігається у браузері. Якщо спільний компонент вже був збережений під час попереднього відвідування сторінки, система може звернутися за меншим об’ємом необ’єднаної частини коду замість того, щоб завантажувати об’єднаний пакет, який містить дублікати вже наявного коду. Це принципово інший підхід, ніж спроба заздалегідь обчислити ідеальний розмір частини коду — це перший випадок, коли інструмент для об’єднання коду відкладає прийняття рішення на момент запиту замість того, щоб фіксувати його під час створення проекту.
Що ми насправді бачили
Команда, яка тестувала це у додатку типу панелі керування, увімкнула функцію generateComponentChunks та спостерігала за тим, що відбувається протягом наступних днів. Початкова швидкість завантаження сторінки майже не змінилась — скоротилася приблизно на 2%. Однак повторні навігації, які становлять основну частину реального використання додатку типу панелі керування, призвели до зниження середнього обсягу JS-коду, який передавався під час кожної навігації, приблизно на 35%. Ось основна цінність цього рішення: це не функція, спрямована на полегшення першого завантаження, а на оптимізацію роботи додатку під ситуацію, коли користувач постійно навігує в ньому протягом десяти хвилин поспіль. Більшість тестів розміру пакету даних, які існують в Інтернеті, вимірюють зовсім іншу ситуацію, яка не відображає реального способу використання додатків із складною внутрішньою навігацією.
Та сама команда також переглянула параметр maxChunkCountPerGroup для маркетингового сайту, який знаходиться в тому ж monorepo, знизивши його значення з 40 до 24. На цьому сайті переважно використовуються сеанси з однією сторінкою, тож додаткові запити до мережі, які генерувалися через більш дрібну структуру чанків, були суто зайвими та не приносили жодної користі. Це був той самий інструмент, але з протилежною налаштуваністю, і обидві опції були правильними для відповідних ситуацій. Мабуть, саме в цьому полягає справжній урок: не існувало жодної універсально правильної стратегії чанкінгу, Turbopack просто робив цей вибір замість користувача, не запитуючи його думки.
Як це порівнюється зі старими налаштуваннями Webpack
Для тих, хто роками боровся з параметром splitChunks.cacheGroups, найближчим аналогом у цій новій системі є maxMergeChunkSize, і на відміну від багатьох іменованих параметрів Webpack, цей працює саме так, як і має. Конфігураційна система Webpack пропонувала десяток слабко задокументованих налаштувань, які взаємодіяли одне з одним у способи, які ніхто не міг повністю зрозуміти, і багато команд опинялися з конфігураційними файлами, залишеними колишніми інженерами, які всі боялися змінювати. Підхід Turbopack є навмисно більш обмеженим. Тут є два-три параметри замість десятка, що жертвує певною гнучкістю заради реально незвичайних ситуацій, але водночас скорочує час від моменту помічення, що „наш бандл виглядає дивно“, до моменту, коли стає зрозуміло, яке саме число потрібно скоригувати. Ця жертва є прийнятною, особливо для тих, хто не хоче...
Є ще одна відмінність, яку варто окремо зазначити. Webpack у значній мірі змушував вас думати про розділення коду на чанки під час збірки, уважно вивчаючи результати файлу stats.json та намагаючись передбачити, що насправді буде у кеші браузера справжнього відвідувача. Розділення коду на чанки на рівні компонентів у Turbopack переносить частину цих роздумів у момент виклику, де час виконання може перевірити фактичний стан кешу замість того, щоб здогадуватися про нього заздалегідь. Це справжня архітектурна зміна, а не просто перейменування прапорця конфігурації.
Недоліки
Ця функція досі позначена як експериментальна, і в документації прямо про це зазначено. Одна команда стикнулася з ситуацією, коли динамічно імпортована бібліотека для створення графіків була розділена таким чином, що під час розгортання на продакшені тимчасово виникла помилка „Не вдалося завантажити частину“, що є знайомою проблемою stale-chunk-reference, яка впливає практично на кожен інструмент для об’єднання коду з підтримкою розділення. Повне оновлення сторінки вирішило проблему для користувачів, у кого вона виникла, але це є корисним нагадуванням про те, що більш дрібне розділення коду збільшує ймовірність виникнення саме цього типу помилок, а не зменшує її.
Є ще проблема налаштування цих параметрів без реальних даних про трафік, що по суті є припущеннями. Варто стримувати бажання змінювати стандартні налаштування у новому проекті, поки у вас немає фактичної аналітики навігації, яка покаже, чи ваші відвідувачі зазвичай користуються сайтом лише один раз, чи це ті, хто протягом однієї сесії переходить між десятком чи більше маршрутів. Якщо таких даних ще немає, це одна з найкращих причин почати їх збір перед тим, як змінювати будь-які налаштування кешування. Налаштування «наосліп» несе реальний ризик отримати гірший показник ефективності кешу, ніж давали б стандартні налаштування, і такий регрес відбувається непомітно. Ніхто не подає звіт про помилку під назвою «навігація працює трохи повільніше» — вони просто перестають повертатися.
Чи варто це увімкнути?
У сценарії панелі керування відповідь була «так», і налаштування залишилося увімкненим. Для маркетингового сайту було скориговано одне число, і на цьому все закінчилося. Жоден з цих заходів не виправляє фундаментально пошкоджену програму, і якщо ваш пакет занадто великий через залежність, яка вам насправді не потрібна, жодна стратегія часткового оброблення не допоможе вибратися з цієї ситуації. Ця функція пропонує спрямоване вирішення конкретної проблеми, що є свіжим підходом порівняно з багатьма функціями інструментів для створення проектів, які випускаються цього року; така думка має вагу, особливо з боку тих, хто провів багато вечорів, намагаючись усунути саме такі помилки.