Заявка
Mobile · CWV

Code Splitting

Разделение JS-бандла на чанки

Code Splitting — это дробление JavaScript-бандла на отдельные части (чанки), которые загружаются по мере надобности. Ускоряет первую загрузку и улучшает Core Web Vitals за счёт меньшего объёма кода при старте.

Code Splitting — это приём, при котором сборщик делит единый JavaScript-файл на несколько независимых чанков, а браузер подтягивает их только когда код реально нужен для экрана или действия. Что это значит на практике: при первой загрузке страница получает не весь скрипт приложения, а лишь минимум для отрисовки видимой части.

Разбиение бандла напрямую задевает Core Web Vitals: чем меньше JS парсит и выполняет браузер на старте, тем раньше наступает интерактивность и стабильнее INP. Настраивается техника через динамический импорт import(), а в связке с React — через React.lazy и Suspense. Результат виден в PageSpeed Insights и в панели Coverage браузера.

§ 01Зачем нужно разделение кода

Монолитный бандл заставляет браузер скачать, распарсить и выполнить весь JS до того, как страница станет отзывчивой. На мобильном устройстве со слабым процессором это растягивает время до интерактивности на секунды. Дробление кода на части решает задачу адресно: маршрут корзины грузится при переходе в корзину, тяжёлый график — при открытии дашборда. Для чего нужно: сократить объём скрипта при старте и снять с главного потока лишнюю работу по парсингу.

§ 02Как устроен механизм

Сборщик строит граф зависимостей и в точке динамического импорта разрывает его, вынося ветку в отдельный чанк. Есть три типовых уровня разбиения:

  • по маршрутам — свой чанк на каждую страницу приложения;
  • по компонентам — модалки, редакторы, виджеты грузятся по клику;
  • по вендорам — библиотеки выносятся в стабильный чанк, который дольше живёт в кеше.

Webpack, Vite и Rollup поддерживают такой раскол из коробки через синтаксис import().

Кейс e-commerce

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%.

Кейс SaaS

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 не изменился — разбиение сделано слишком дробно и чанки надо укрупнить.

Частые ошибки
01
Дробление бандла на десятки мелких чанков. Каждый чанк — отдельный сетевой запрос с задержкой на установку соединения. При избыточном разбиении браузер тратит время на водопад запросов, и TTFB общей загрузки растёт вместо падения — LCP страдает на мобильном 4G. Как правильно: Группируйте связанные модули в логические чанки. Держите вендорный код отдельно от роутовых сплитов и не дробите ниже 20-30 КБ на чанк.
02
Ленивая загрузка компонентов первого экрана. Через lazy-import выносят критичный контент верхней части страницы. В результате пользователь видит спиннер вместо готового блока, а Google фиксирует поздний LCP-элемент — метрика ухудшается, хотя вес стартового бандла упал. Как правильно: Оставляйте above-the-fold статичным в основном бандле. Выносите в динамические импорты только то, что ниже сгиба: модалки, табы, футер.
03
Оценка эффекта по размеру бандла в сборке. Смотрят на цифры webpack-bundle-analyzer и радуются уменьшению, но не меряют полевой LCP и INP. Синтетика в DevTools показывает выигрыш, а реальные сессии в CrUX стагнируют — бизнес не видит роста конверсии. Как правильно: Замеряйте эффект по полевым данным CrUX и Search Console за 28 дней. Сравнивайте LCP и INP до и после релиза, а не только вес чанков.
Часто спрашивают

Code Splitting: ответы на популярные вопросы

Code Splitting простыми словами — что это и зачем?

+

Code Splitting — это дробление одного большого JavaScript-бандла на отдельные части, которые грузятся по мере надобности. Браузеру не нужно тянуть весь код при старте: сначала подгружается только то, что видит пользователь на первом экране. За счёт меньшего объёма при старте страница интерактивна быстрее.

Сборщик режет проект по точкам динамического импорта и создаёт отдельные файлы-чанки. Webpack делает это через import(), Vite и Rollup — автоматически по маршрутам и модулям. При переходе на новый роут браузер запрашивает нужный кусок по требованию, а не заранее.

Дробление кода разбивает бандл на части и откладывает их загрузку до момента использования. Tree shaking же вырезает мёртвый, неиспользуемый код ещё на этапе сборки. Первое управляет когда грузится код, второе — что вообще попадёт в сборку. Их применяют вместе.

Обычно положительно: меньший стартовый объём скриптов сокращает блокировку главного потока и улучшает LCP и INP. По данным Lighthouse, отложенная загрузка тяжёлых модулей нередко снимает 1–3 секунды с Time to Interactive на мобильных. Эффект заметнее на слабых устройствах и медленных сетях.

Зависит от размера кода, но для SPA и сайтов с тяжёлым JS — да, стоит с самого начала. Если бандл меньше 100–150 КБ, выигрыш минимальный и усложнять сборку смысла нет. Практичный старт: разбить приложение по маршрутам через React.lazy или динамические импорты и мерить результат в PageSpeed Insights.

Не нашли ответ?

Спросите Андрея Гусарова — отвечаю в течение 1-2 рабочих дней.

Нажимая, вы соглашаетесь с обработкой данных.