Code Splitting — это приём, при котором сборщик делит единый JavaScript-файл на несколько независимых чанков, а браузер подтягивает их только когда код реально нужен для экрана или действия. Что это значит на практике: при первой загрузке страница получает не весь скрипт приложения, а лишь минимум для отрисовки видимой части.
Разбиение бандла напрямую задевает Core Web Vitals: чем меньше JS парсит и выполняет браузер на старте, тем раньше наступает интерактивность и стабильнее INP. Настраивается техника через динамический импорт import(), а в связке с React — через React.lazy и Suspense. Результат виден в PageSpeed Insights и в панели Coverage браузера.
§ 01Зачем нужно разделение кода
Монолитный бандл заставляет браузер скачать, распарсить и выполнить весь JS до того, как страница станет отзывчивой. На мобильном устройстве со слабым процессором это растягивает время до интерактивности на секунды. Дробление кода на части решает задачу адресно: маршрут корзины грузится при переходе в корзину, тяжёлый график — при открытии дашборда. Для чего нужно: сократить объём скрипта при старте и снять с главного потока лишнюю работу по парсингу.
§ 02Как устроен механизм
Сборщик строит граф зависимостей и в точке динамического импорта разрывает его, вынося ветку в отдельный чанк. Есть три типовых уровня разбиения:
- по маршрутам — свой чанк на каждую страницу приложения;
- по компонентам — модалки, редакторы, виджеты грузятся по клику;
- по вендорам — библиотеки выносятся в стабильный чанк, который дольше живёт в кеше.
Webpack, Vite и Rollup поддерживают такой раскол из коробки через синтаксис import().
LCP 4.8→2.3 c — Интернет-магазин электроники на Next.js вынес корзину и чекаут в динамические чанки через dynamic import и React.lazy. Стартовый бандл ужался с 1.2mb до 480kb, LCP упал с 4.8 до 2.3 с за 5 недель. Замер через PSI и CrUX-75th, отказы снизились на 11%.
TBT −320 мс — B2B SaaS-платформа аналитики на Vue 3 разбила дашборд по route-level чанкам и отложила загрузку графиков через Webpack splitChunks. TBT упал на 320 мс, INP вышел в зелёную зону за 7 недель. Динамика по Lighthouse CI и полю CrUX, регистрации выросли на 9%.
§ 03Как настраивается
Базовая точка входа — заменить статический import Chart from './Chart' на динамический const Chart = React.lazy(() => import('./Chart')) и обернуть его в Suspense с фолбэком. В Next.js ту же роль играет next/dynamic, а маршрутное разбиение включается автоматически по структуре папок. Загрузку чанков по требованию стоит дополнять предзагрузкой критичных веток через rel="prefetch", чтобы переход не упирался в сетевую паузу.
§ 04Как проверить, что работает
Панель Coverage в DevTools показывает долю неиспользованного JS на первом экране — после дробления бандла она падает. Вкладка Network демонстрирует, что чанк маршрута грузится отдельным запросом только при переходе. PageSpeed Insights и отчёт CrUX отражают сдвиг по метрикам загрузки и INP на реальных пользователях. Если после оптимизации загрузки скриптов число мелких запросов взлетело, а Total Blocking Time не изменился — разбиение сделано слишком дробно и чанки надо укрупнить.