Code splitting за маршрутами: де ламається швидкість завантаження
Єдиний бандл перестає бути безпечним рівно в той момент, коли застосунок переростає десяток маршрутів.
Збирач складає в один файл і стартову сторінку, і адмінку, і розділ звітів — браузер зобов'язаний завантажити й розібрати все це до першого рендерингу, навіть якщо користувач відкрив лише форму входу.
Розмір бандла зростає нелінійно: кожна нова важка залежність тягне за собою транзитивні модулі, а дерево залежностей між маршрутами майже завжди перетинається.
Далі вмикається ефект дублювання. Якщо розділи збираються незалежно й без спільної конфігурації, один і той самий модуль потрапляє в кілька чанків — браузер завантажує його повторно, кеш не спрацьовує, і замість економії трафіку виходить регрес.
Час до інтерактивності на важких маршрутах зростає: поки розбирається непотрібний код, кнопки не реагують, а користувач вважає, що інтерфейс завис.
Ліниве завантаження модулів за маршрутами вирішує це на рівні архітектури, а не косметики.
Точки розділення ставляться на межах маршрутів, спільні залежності виносяться в окремий чанк, а передзавантаження підключається для тих переходів, які користувач здійснює з високою ймовірністю. Тоді стартова сторінка віддає лише свій код, а решта підтягується за фактом навігації.
Розбираємо, як це закривається: де саме ставити межі розділення, як не породити дублі й чим вимірювати результат до і після.
Що дає розділення збірки за маршрутами
Розділення збірки за маршрутами — це коли кожна сторінка застосунку завантажує лише свій код, а не весь проєкт цілком. Замість одного стартового бандла, який тягне на перший екран усе одразу, збирач нарізає код на чанки й підвантажує їх у міру переходу користувача.
У результаті стартовий бандл скорочується в рази: на перший екран приходить лише те, що потрібно для його відрисовки, а важкі розділи — аналітика, редактори, карти — підтягуються пізніше, коли користувач до них дійшов.
Час завантаження сторінки падає не лінійно, а стрибком: браузер швидше завантажує менший обсяг, раніше починає парсити й відрисовувати розмітку, і перша відрисовка настає помітно раніше.
Особливо це видно на мобільних мережах і на повільних каналах, де кожен зайвий кілобайт стартового бандла напряму відсуває момент, коли користувач бачить контент.
Кешування чанків працює на плюс до швидкості: код маршруту змінюється рідше, ніж загальний каркас застосунку, тому при оновленні одного розділу браузер перевикористовує вже завантажені чанки решти.
Користувач отримує свіжу версію сторінки, не перекачуючи все заново, а повторні візити проходять майже миттєво.
Ізоляція відмов — окрема вигода, яку помічають уже в експлуатації. Якщо один маршрут падає через помилку у своєму коді, решта продовжують працювати: збій локалізовано всередині чанка, а не валить весь застосунок. Це спрощує і пошук причини, і відкат — достатньо повернути попередню версію конкретного розділу.
Перевіряємо ефект за метриками: розмір стартового бандла до і після, час до першої відрисовки, частка маршрутів, винесених в окремі чанки, і кількість повторних завантажень при оновленні релізу.
Якщо стартовий бандл не зменшився або чанки дублюють загальний код — розділення зроблено формально, і ми це бачимо на цифрах, а не на словах.
Способи організації розділення: порівняння підходів
Розділення коду — це компроміс між швидкістю першого рендерингу та кількістю мережевих запитів. Чим дрібніше нарізані частини, тим швидше стартує сторінка, але тим більше накладних витрат на їх довантаження та ризик «водоспаду» під час переходів. Нижче — три робочі схеми, між якими реально обирають на практиці.
| Варіант | Коли підходить | Обмеження |
|---|---|---|
| Розділення за маршрутами | Великий застосунок із чіткими розділами: каталог, кабінет, адмінка. Кожен маршрут — окрема точка входу через динамічний імпорт | Спільний код дублюється між чанками, якщо не винести його в спільний модуль; перехід між розділами дає затримку без передзавантаження |
| Розділення за компонентами | Важкі віджети в межах однієї сторінки: редактор, графіки, карти, модальні вікна | Багато дрібних запитів; без грамотного передзавантаження чанків користувач бачить «мерехтіння» під час першої взаємодії |
| Гібридна схема | Продакшн-застосунки, де важливі і старт, і переходи: маршрути як основа, важкі компоненти — окремими чанками | Складніша конфігурація збірки та правила передзавантаження; потрібен контроль, щоб чанки не розросталися |
Як обирають на практиці
Для більшості проєктів базою стає розділення за маршрутами: воно дає передбачувану структуру та зрозумілу точку входу на кожен розділ. Поверх нього точково виділяють важкі компоненти — так перший рендеринг залишається швидким, а рідкісні сценарії не тягнуть за собою весь код.
Передзавантаження чанків закриває головний мінус схеми — затримку під час переходу. Посилання на ймовірні наступні маршрути прогріваються заздалегідь, і користувач не чекає на завантаження в момент кліку.
Гібридна схема вимагає дисципліни: правила розділення фіксуються в конфігурації збірки, а розмір чанків перевіряється на кожному релізі, інакше розділення перетворюється на джерело регресій.
Етапи впровадження розділення збірки
Впровадження розділення збірки — це не «увімкнути галочку в конфізі», а послідовність інженерних кроків із перевірюваним результатом на кожному. Нижче — процес, за яким ми ведемо роботи: від аудиту поточного бандла до передачі з метриками до/після.
- Аудит збірки. Розбираємо поточний бандл за чанками: дивимося, що потрапляє до стартового завантажувача, які залежності тягнуть за собою важкі бібліотеки, де дублюються модулі. На виході — карта «що вантажиться завжди, а що потрібно лише на конкретному екрані» та список очевидних кандидатів на винесення.
- Схема меж маршрутів. Проєктуємо, де саме розрізати застосунок: за маршрутами верхнього рівня, за вкладеними розділами або за сценаріями всередині одного екрана. Фіксуємо спільні модулі (авторизація, шапка, дизайн-система) в окремий спільний чанк, щоб вони не дублювалися в кожному шматку.
- Налаштування збирача. Переводимо схему в конфігурацію: динамічні імпорти на межах маршрутів, іменування чанків, винесення вендорних залежностей, передзавантаження ймовірних наступних екранів. Окремо налаштовуємо поведінку при обриві завантаження чанка — користувач не повинен бачити білий екран.
- Перевірка метрик. Порівнюємо до/після: обсяг стартового бандла, час до першого рендера, кількість запитів на перший екран, частка коду, що завантажується на вимогу. Дивимося не середні, а найгірший сценарій — повільне з'єднання та холодний кеш.
- Регрес і крайові випадки. Прогоняємо переходи між маршрутами, прямі заходи за глибокими посиланнями, повернення за історією, відкат на попередню версію застосунку при незакритих чанках.
- Документація та передача. Віддаємо схему меж, конфігурацію збирача та правила додавання нових маршрутів, щоб наступний розробник не зламав розбиття одним рядком імпорту.
Кожен етап закінчується артефактом, який можна перевірити, а не усним «готово».
Конфігурації, схеми маршрутів і регламенти на виході
Розбиття за маршрутами живе не в коді, а в конфігурації та домовленостях.
Якщо межі чанків задані лише «на око» в одному файлі збирача, через пару релізів ніхто не згадає, чому маршрут оплати завантажується разом із каталогом, а новий розробник додасть сторінку до спільного бандла. Тому ми фіксуємо результат в артефактах, які читаються без нас.
- Конфігурація збірки — правила розбиття: які маршрути отримують окремий чанк, що виноситься до спільного (спільні компоненти, клієнт API, стилі), як називаються файли. З коментарями, чому межа проведена саме тут.
- Карта маршрутів — таблиця «маршрут → чанк → що в ньому лежить → коли завантажується». За нею видно, де перетинаються залежності й який маршрут тягне за собою зайве.
- Правила кешування — заголовки для хешованих чанків і для точок входу: незмінні файли кешуються надовго, точка входу переперевіряється. Окремо — поведінка під час викатки, щоб користувач не отримав стару точку входу з новими чанками.
- Передзавантаження та пріоритети — які маршрути підвантажуються заздалегідь за навігацією, які за наведенням, які лише за кліком. Це усвідомлене рішення, а не побічний ефект.
- Документація — як читати карту маршрутів, де дивитися розміри чанків, що робити при зростанні бандла.
- Регламент додавання маршрутів — покроковий чек-ліст: завести маршрут, призначити чанк, перевірити перетини, оновити карту, прогнати перевірку розмірів у збірці.
- Перевірки у збірці — автоматичний контроль: якщо чанк перевищив поріг або маршрут потрапив до спільного бандла, збірка попереджає. Правило працює без нагадувань.
Такий набір переживає зміну підрядника: межі чанків, кеш і порядок додавання сторінок описані явно, а не відновлюються за історією комітів.
Кейс: скорочення стартового завантаження особистого кабінету
Особистий кабінет банківського клієнта: єдиний стартовий бандл на 2.8 МБ, час до інтерактивності на середньому мобільному каналі — 6.4 с.
На повільних каналах (3G, перевантажений Wi-Fi у торгових центрах) частка відмов до першої взаємодії сягала 23% — користувач бачив білий екран і йшов раніше, ніж застосунок встигав відмалюватися.
Розібрали маршрути за частотою використання: вхід, баланс, історія операцій, перекази, налаштування. Кожен маршрут отримав власну точку входу, спільний код винесено в окремий чанк, важкі віджети (графіки, редактор реквізитів) підвантажуються на вимогу через динамічний імпорт.
Передзавантаження сусідніх маршрутів вмикається лише після першої взаємодії — на повільному каналі це не забирає смугу в критичного коду.
Метрики до і після
| Метрика | До | Після |
|---|---|---|
| Стартовий бандл | 2.8 МБ | 640 КБ |
| Час до інтерактивності (середній мобільний) | 6.4 с | 2.1 с |
| Відмови до першої взаємодії | 23% | 7% |
Стартовий бандл скоротився в 4.4 раза — на екран входу тепер вантажиться лише форма авторизації та спільний рантайм. Час до інтерактивності впав утричі, а частка відмов на повільних каналах — з 23% до 7%. Конверсія в перший вхід зросла на 11 в.п. без змін у самому сценарії авторизації.
Окремо налаштували метрики продуктивності в реальному часі: розбивка за маршрутами, час завантаження кожного чанка, частка користувачів, у яких інтерактивність не настає за 4 с. Це дозволяє ловити регресії за кожного релізу, а не через місяць за скаргами. Інструмент — webpack-bundle-analyzer для контролю складу бандлів на етапі збірки.
Чим розділення за маршрутами відрізняється від розділення за компонентами?
Розділення за маршрутами — це стратегія, за якої точка входу в застосунок і кожен екран верхнього рівня збираються в окремий чанк, а перехід між розділами підвантажує потрібний фрагмент на вимогу.
Розділення за компонентами працює на рівень нижче: дробляться окремі блоки всередині одного екрана — модальні вікна, важкі віджети, редактори.
Різниця не в інструменті, а в гранулярності чанків і в тому, хто керує моментом завантаження: за маршрутного поділу це робить навігація, за компонентного — логіка самого інтерфейсу.
Гранулярність безпосередньо визначає накладні витрати. Кожен чанк — це окремий мережевий запит зі своїм заголовком, рукостисканням і часом до першого байта. П'ять-сім маршрутних чанків дають передбачувану картину: користувач завантажує рівно той розділ, куди зайшов.
Дроблення за компонентами легко сягає десятків дрібних файлів, і на повільному з'єднанні сумарні накладні витрати починають з'їдати виграш від меншого обсягу — особливо коли компоненти вантажаться каскадом, один за одним.
Кеш браузера — другий вододіл. Маршрутні чанки змінюються рідко й точково: правка в одному розділі не інвалідує решту, тому за наступного деплою користувач перевантажує лише зачеплений фрагмент, а все інше береться з кешу.
За компонентного поділу хеші файлів перераховуються частіше, і межа між «оновилося» та «залишилося незмінним» розмивається — зростає частка повторно завантаженого.
На практиці стратегії не конкурують, а вкладаються одна в одну. Маршрут задає великі стабільні чанки й передбачуваний кеш, а всередині важкого екрана точково виділяються окремі компоненти — ті, що потрібні не одразу або рідко.
Такий порядок тримає кількість запитів під контролем і зберігає виграш від відкладеного завантаження там, де він реально помітний.
Технічні сумніви перед впровадженням
Розділення збірки за маршрутами змінює те, як браузер отримує код: замість одного великого файлу він підвантажує чанки в міру переходу користувача. Це зачіпає індексацію, кешування та відкат релізів — розберемо кожен пункт окремо.
Чи не постраждає SEO, якщо частина коду завантажується на вимогу?
Пошукові роботи виконують JavaScript і бачать відрендерений вміст, тому відкладене завантаження чанків саме по собі не приховує контент. Ризик виникає в іншому: якщо точка входу маршруту підвантажується лише за кліком, робот може не дійти до неї.
Тому критичні маршрути ми віддаємо із серверним рендерингом або пререндером, а посилання в розмітці залишаємо справжніми — тоді індексація не залежить від того, чи спрацював перехід.
Після впровадження звіряємо карту сайту з фактично проіндексованими адресами та покриття в панелі вебмайстра.
Як працює передзавантаження і чи не з'їдає воно вигоду від розділення?
Передзавантаження запускається на простоюючому каналі та за наміром: наведення або торкання посилання, поява його в зоні видимості. Пріоритет у чанка поточного маршруту, решта довантажується фоном.
Якщо передзавантажувати все підряд, ефект розділення втрачається — тому список прогріву обмежуємо 2-3 ймовірними переходами і переглядаємо за даними аналітики.
Що з інвалідацією кешу при оновленні?
Імена чанків прив'язані до вмісту, тому новий реліз отримує нові адреси, а старі залишаються в кеші браузера і не заважають. Точку входу та розмітку віддаємо із забороною довгого кешування — інакше користувач застрягне на попередній версії.
Під час викладки стежимо, щоб посилання на чанки та самі файли потрапляли в сховище однією операцією.
Як відкочувати зміни, якщо реліз дав регресію?
Відкат — це повернення попередньої точки входу та попереднього набору чанків, вони не перезаписуються. Тримаємо кілька останніх збірок, тому повернення займає хвилини і не потребує перезбірки.
Перед викладкою проганяємо димові перевірки за маршрутами, щоб ловити обриви завантаження чанків до користувачів.
Якщо якийсь із цих сценаріїв у вас уже болів — опишіть задачу, повернемося з оцінкою та планом під ваш стек.
Обговоримо ваше завдання щодо розділення збірки
Щоб повернутися з оцінкою та планом впровадження, а не із загальними словами, нам потрібні вихідні дані щодо вашого застосунку. Чим точніші вхідні дані, тим менше припущень у кошторисі й тим реалістичніші строки. Нижче — що уточнюємо і що очікуємо від вас.
Що уточнимо на нашій стороні:
- поточну структуру збірки: один спільний бандл чи вже є ручне розділення на кілька файлів;
- карту маршрутів і точок входу — які екрани завантажуються першими, які рідко;
- обсяг і склад залежностей: що потрапляє до стартового чанку і чи можна це відкласти;
- наявність серверного рендерингу та те, як він поєднується з відкладеним завантаженням;
- вимоги до поведінки під час переходів: індикатори завантаження, попереднє завантаження ймовірних маршрутів;
- поточний процес релізів і те, як викочуються зміни без простою.
Що потрібно від вас для оцінки робіт:
- доступ до репозиторію або вивантаження конфігурації збірки та карти маршрутів;
- опис цільових метрик: час до інтерактивності, вага стартового чанку, частка відкладеного коду;
- перелік браузерів і пристроїв, які обов'язково мають підтримуватися;
- обмеження щодо інфраструктури: де хоститься статика, чи є CDN, як налаштоване кешування.
Як повернемося з планом впровадження. Розкладемо роботу на етапи: аудит поточної збірки, проєктування меж розділення за маршрутами, налаштування відкладеного завантаження та попереднього завантаження, перевірка поведінки під час навігації та відкат у разі регресії.
До кожного етапу додамо очікуваний ефект у вимірюваних величинах — вага стартового чанку, час до інтерактивності, частка коду, що завантажується на вимогу.
Опишіть завдання у вільній формі — що це за застосунок, які маршрути і що турбує в поточному завантаженні. Ми повернемося з оцінкою робіт і планом впровадження за етапами.







