Оптимізація 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 load сторінок
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 наступної сторінки при наведенні на посилання
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, заміна бібліотек.