Как проанализировать и уменьшить размер JavaScript-бандла
Продвинутое4 минРедакция Innovialabs
Коротко
Сначала посмотрите на бандл через analyzer и найдите самые крупные модули, затем по очереди включите tree shaking, замените тяжёлые зависимости, сузьте полифиллы и перенесите лишнюю работу на сервер, каждый раз фиксируя размер до и после.
Короткий ответ
Размер бандла уменьшают не догадками, а по данным. Рабочий цикл:
- Снять базу — сколько JS (сжатого и несжатого) грузится на ключевых страницах.
- Найти крупнейшие модули через bundle analyzer.
- Убрать или заменить их одним из приёмов ниже.
- Перемерить и записать результат.
Важно смотреть не только на байты по сети: несжатый JS браузер ещё парсит и выполняет, и на слабых телефонах это заметно дольше, чем загрузка.
Шаг 1. Анализ бандла
Инструменты строят карту (treemap): каждый прямоугольник — модуль, площадь — его размер.
| Стек | Инструмент |
|---|---|
| webpack | webpack-bundle-analyzer |
| Next.js | @next/bundle-analyzer |
| Vite / Rollup | rollup-plugin-visualizer |
| Любой с source maps | source-map-explorer |
Пример для Next.js:
// next.config.js
const withBundleAnalyzer = require("@next/bundle-analyzer")({
enabled: process.env.ANALYZE === "true",
});
module.exports = withBundleAnalyzer({});
Запуск: ANALYZE=true npm run build. Что искать в отчёте:
- одну библиотеку, занимающую непропорционально много места;
- дубликаты — две версии одного пакета;
- код, который нужен одной странице, но попал в общий чанк;
- локали, иконки и данные, загруженные целиком.
Шаг 2. Tree shaking
Tree shaking — удаление неиспользуемых экспортов при сборке. Работает, когда:
- код и зависимости используют ES-модули (
import/export), а не CommonJS; - импорты именованные:
import { debounce } from "lodash-es", а не весь пакет; - в
package.jsonбиблиотеки корректно указанsideEffects.
Проверка простая: импортируйте одну функцию и посмотрите в analyzer, сколько кода библиотеки попало в бандл. Если вся — tree shaking не работает.
Шаг 3. Замена тяжёлых библиотек
Частые кандидаты и типовые альтернативы:
- Даты: крупные библиотеки с локалями →
date-fns(модульный импорт),dayjsили встроенныйIntl.DateTimeFormat. - Утилиты: полный
lodash→ отдельные функции изlodash-esили нативные методы массивов и объектов. - Иконки: импорт всего набора → импорт отдельных иконок или SVG-спрайт.
- HTTP-клиент: если нужны простые запросы, часто хватает встроенного
fetch. - Графики и редакторы: не заменять, а загружать лениво через dynamic import.
Перед добавлением новой зависимости проверяйте её размер (например, на bundlephobia) и поддержку tree shaking.
Шаг 4. Лишние полифиллы и транспиляция
Если сборка ориентирована на старые браузеры, в бандл попадают полифиллы и громоздко транспилированный код.
- Уточните целевые браузеры в browserslist по реальной аудитории из аналитики.
- Не подключайте полифиллы целиком «на всякий случай» — только для фич, которые реально используются и не поддерживаются вашими целями.
- Проверьте, не тянет ли какая-то зависимость собственные полифиллы.
Шаг 5. Перенос работы на сервер
Самый эффективный JS — тот, который не отправляется в браузер.
- Серверный рендеринг и React Server Components: компоненты без интерактивности рендерятся на сервере, их код и зависимости (например, парсер Markdown) не попадают в клиентский бандл.
- Форматирование, сортировку и фильтрацию больших данных выполняйте на сервере и отдавайте готовый результат.
- Держите
"use client"как можно ниже по дереву компонентов, чтобы не тянуть на клиент лишнее.
Как фиксировать «до и после»
Записывайте результат каждого шага в одну таблицу — так видно, что дало эффект, а что нет:
| Изменение | Начальный JS (gzip/brotli) | Начальный JS (несжатый) | Lighthouse TBT | INP (field) |
|---|---|---|---|---|
| База | — | — | — | — |
| Tree shaking иконок | — | — | — | — |
| Замена библиотеки дат | — | — | — | — |
| Перенос парсера на сервер | — | — | — | — |
Заполняйте её своими замерами при одинаковых условиях (тот же билд-режим, та же страница, тот же троттлинг). Чтобы размер не рос обратно, добавьте бюджет в CI — например, size-limit или проверку в Lighthouse CI.
Частые ошибки
- Оптимизировать по размеру
node_modules, а не по реальному бандлу. - Смотреть dev-сборку вместо production.
- Удалять зависимость, но оставлять её импорт в общем модуле.
- Не следить за размером после релиза — бандл постепенно растёт снова.