Оптимизация JavaScript-бандла сайта (code splitting, tree shaking)

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Оптимизация JavaScript-бандла сайта (code splitting, tree shaking)
Средняя
~2-3 рабочих дня
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Оптимизация JavaScript-бандла

Тяжёлый JS — главная причина плохого INP и медленного FCP для SPA. Браузер должен загрузить, распарсить и выполнить весь JS до рендеринга. Оптимизация бандла — разбивка на части, удаление неиспользуемого кода, отложенная загрузка.

Анализ бандла

# Vite — визуализация через rollup-plugin-visualizer
npm install -D rollup-plugin-visualizer
// vite.config.ts
import { visualizer } from 'rollup-plugin-visualizer';

export default defineConfig({
    plugins: [
        visualizer({
            filename: 'dist/stats.html',
            open: true,
            gzipSize: true,
        })
    ]
});

После сборки открывается интерактивная карта бандла. Ищем:

  • Крупные библиотеки (moment.js, lodash — часто заменимы)
  • Дублирование зависимостей
  • Библиотеки импортированные целиком вместо нужной функции

Code Splitting — разбивка по маршрутам

// React Router v6 — lazy loading страниц
import { lazy, Suspense } from 'react';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';

const ProductCatalog = lazy(() => import('./pages/ProductCatalog'));
const ProductDetail  = lazy(() => import('./pages/ProductDetail'));
const Cart           = lazy(() => import('./pages/Cart'));
const Checkout       = lazy(() => import('./pages/Checkout'));

const router = createBrowserRouter([
    { path: '/catalog',           element: <Suspense fallback={<PageSkeleton />}><ProductCatalog /></Suspense> },
    { path: '/products/:slug',    element: <Suspense fallback={<PageSkeleton />}><ProductDetail /></Suspense> },
    { path: '/cart',              element: <Suspense fallback={<PageSkeleton />}><Cart /></Suspense> },
    { path: '/checkout',          element: <Suspense fallback={<PageSkeleton />}><Checkout /></Suspense> },
]);

Динамический импорт тяжёлых компонентов

// Редактор, графики, карты — загружать только при необходимости
const RichTextEditor = lazy(() => import('./components/RichTextEditor'));
const Chart          = lazy(() => import('./components/Chart'));
const YandexMap      = lazy(() => import('./components/YandexMap'));

function ProductForm() {
    const [showEditor, setShowEditor] = useState(false);

    return (
        <>
            <button onClick={() => setShowEditor(true)}>
                Добавить описание
            </button>
            {showEditor && (
                <Suspense fallback={<div>Загрузка редактора...</div>}>
                    <RichTextEditor />
                </Suspense>
            )}
        </>
    );
}

Tree shaking — удаление неиспользуемого кода

// Плохо: импорт всего lodash (~70кБ gzip)
import _ from 'lodash';
const sorted = _.sortBy(products, 'price');

// Хорошо: импорт только нужной функции
import sortBy from 'lodash/sortBy';
const sorted = sortBy(products, 'price');

// Ещё лучше: нативный JS
const sorted = [...products].sort((a, b) => a.price - b.price);

// date-fns вместо moment.js
import { format, addDays } from 'date-fns';  // tree-shakeable
import { ru } from 'date-fns/locale';

Замена тяжёлых библиотек

Библиотека Замена Экономия
moment.js (72кБ) date-fns (только нужные функции) ~60кБ
lodash (70кБ) lodash-es + tree-shaking ~50кБ
axios (13кБ) native fetch 13кБ
jquery (87кБ) Нативный JS 87кБ
react-icons (все иконки) Только нужные из @heroicons 100–500кБ

Vite: ручное разбиение чанков

// vite.config.ts
export default defineConfig({
    build: {
        rollupOptions: {
            output: {
                manualChunks: {
                    // Vendor chunk — редко меняется, долго кешируется
                    'vendor-react':  ['react', 'react-dom', 'react-router-dom'],
                    'vendor-ui':     ['@radix-ui/react-dialog', '@radix-ui/react-dropdown-menu'],
                    'vendor-query':  ['@tanstack/react-query'],
                    'vendor-forms':  ['react-hook-form', 'zod', '@hookform/resolvers'],
                    'vendor-charts': ['recharts'],
                },
            }
        },
        chunkSizeWarningLimit: 500,
    }
});

Preload критичных чанков

// Prefetch следующей страницы при hover на ссылку
function PrefetchLink({ to, children }) {
    const prefetch = () => {
        import(`./pages/${to}`).catch(() => {});
    };

    return (
        <Link to={to} onMouseEnter={prefetch} onFocus={prefetch}>
            {children}
        </Link>
    );
}

Метрики размера бандла

Целевые значения для интернет-магазина:

Чанк Цель (gzip)
Первоначальный JS (critical path) < 50 кБ
React + React DOM ~42 кБ (фиксировано)
Страница каталога < 30 кБ
Карточка товара < 20 кБ
Корзина/Оформление < 40 кБ

Мониторинг размера в CI

# .github/workflows/bundle-size.yml
- name: Check bundle size
  run: |
    npm run build
    MAIN_JS=$(ls dist/assets/index-*.js | xargs stat -c%s | head -1)
    if [ "$MAIN_JS" -gt 200000 ]; then
      echo "Bundle too large: ${MAIN_JS} bytes"
      exit 1
    fi

Срок оптимизации: 2–4 дня: анализ, code splitting, замена библиотек.