Code Splitting: разделение кода
Code Splitting — техника разбиения JavaScript bundle на несколько chunk-файлов, загружаемых по требованию, а не единым монолитом при первом запросе.
Зачем нужно
SPA без code splitting загружает весь JS-код при первом посещении, даже если пользователь видит только главную страницу. Код админ-панели, графиков, редактора скачивается сразу. Code splitting позволяет загружать только то, что нужно сейчас: стартовый bundle минимален, остальное — по запросу. Это напрямую влияет на Time to Interactive (TTI) и Core Web Vitals.
Где используется
- Webpack
SplitChunksPlugin— автоматическое разделение vendor/common chunks - Vite — автоматическое разделение по динамическим импортам
React.lazy+import— разделение по маршрутам и тяжёлым компонентам- Next.js — автоматическое разделение по страницам
Динамический импорт
// Статический импорт — попадает в основной bundle
import HeavyLibrary from 'heavy-library'; // всегда загружается
// Динамический импорт — создаёт отдельный chunk
// Загружается только когда выполнится этот код
const module = await import('./heavy-module');
// Практический пример: загрузка библиотеки по требованию
async function exportToExcel(data) {
// xlsx не загружается пока пользователь не нажал "Экспорт"
const { utils, writeFile } = await import('xlsx');
const wb = utils.book_new;
utils.book_append_sheet(wb, utils.json_to_sheet(data));
writeFile(wb, 'export.xlsx');
}
React.lazy + Suspense (разделение по маршрутам)
import { lazy, Suspense } from 'react';
// Каждая страница — отдельный chunk
const HomePage = lazy( => import('./pages/HomePage'));
const Dashboard = lazy( => import('./pages/Dashboard'));
const AdminPanel = lazy( => import('./pages/AdminPanel'));
// AdminPanel.js загрузится только при переходе на /admin
function App() {
return (
<Suspense fallback={<PageSkeleton />}>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/admin" element={<AdminPanel />} />
</Routes>
</Suspense>
);
}
Webpack: настройка разделения chunks
// webpack.config.js
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
// Vendor chunk — React, React DOM и т.д.
vendor: {
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
name: 'vendor',
priority: 10,
},
// Common chunk — код, используемый в 2+ местах
common: {
minChunks: 2,
name: 'common',
priority: 5,
},
},
},
},
};
Анализ bundle
# Webpack Bundle Analyzer — визуализация что сколько весит
npm install --save-dev webpack-bundle-analyzer
# В webpack.config.js:
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
plugins: [new BundleAnalyzerPlugin],
# Vite — встроенный анализ
npx vite build --report
Результат до и после
До code splitting:
bundle.js — 2.5 MB (всё приложение)
Первая загрузка: 2.5 MB
После code splitting:
main.js — 150 KB (React + роутер + главная)
vendor.js — 200 KB (React, React DOM — кешируется)
dashboard.js — 300 KB (загружается при переходе)
admin.js — 800 KB (загружается только для админов)
Первая загрузка: 350 KB (7× быстрее)
Частые ошибки
- Разделение слишком мелких модулей — HTTP overhead от запроса дороже выигрыша; разделяйте chunk не менее ~20 KB.
- Нет Suspense fallback —
React.lazyбезSuspenseпадает с ошибкой. - Нет анализа bundle — делают code splitting «вслепую»; используйте Bundle Analyzer чтобы видеть реальный эффект.
- Разделяют vendor chunk неправильно — React и React DOM должны быть в одном chunk; их разделение создаёт проблемы.
Связанные темы
- _MOC SPA
- Lazy Loading маршрутов
- Bundle vs Unbundle -- разница
- HMR -- Hot Module Replacement
- Suspense и Concurrent Features