5. Code Splitting
Code Splitting — это разделение бандла на части, которые загружаются по необходимости. Вместо одного огромного JS файла — много маленьких.
Зачем нужен Code Splitting?
Заголовок раздела «Зачем нужен Code Splitting?»❌ Без code splitting:Загружаем 2MB JS → пользователь ждёт → видит страницу
✅ С code splitting:Загружаем 200KB core → пользователь видит страницуЗагружаем остальное → когда нужноДинамические импорты (ES2020)
Заголовок раздела «Динамические импорты (ES2020)»// ❌ Статический импорт — всё в одном бандлеimport { Chart } from './chart'; // тяжёлая библиотека
// ✅ Динамический импорт — загружается при необходимостиconst loadChart = async () => { const { Chart } = await import('./chart'); return new Chart(/* ... */);};
// Загружаем только при кликеbutton.addEventListener('click', async () => { const { openModal } = await import('./modal'); openModal();});
// С обработкой ошибокasync function loadFeature() { try { const { Feature } = await import('./feature'); Feature.init(); } catch (error) { console.error('Failed to load feature:', error); }}React: lazy() и Suspense
Заголовок раздела «React: lazy() и Suspense»import { lazy, Suspense } from 'react';
// ✅ Ленивая загрузка компонентаconst Dashboard = lazy(() => import('./Dashboard'));const UserProfile = lazy(() => import('./UserProfile'));const HeavyChart = lazy(() => import('./HeavyChart'));
function App() { return ( <Suspense fallback={<div>Загрузка...</div>}> <Dashboard /> </Suspense> );}
// ✅ С роутеромimport { Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./pages/Home'));const About = lazy(() => import('./pages/About'));const Blog = lazy(() => import('./pages/Blog'));
function AppRoutes() { return ( <Suspense fallback={<PageLoader />}> <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> <Route path="/blog" element={<Blog />} /> </Routes> </Suspense> );}Webpack — Настройка
Заголовок раздела «Webpack — Настройка»module.exports = { optimization: { splitChunks: { chunks: 'all', cacheGroups: { // Выносим vendor в отдельный чанк vendor: { test: /[\\/]node_modules[\\/]/, name: 'vendors', chunks: 'all', }, // Выносим shared components common: { name: 'common', minChunks: 2, chunks: 'async', priority: 10, }, }, }, // Runtime в отдельный файл runtimeChunk: 'single', },};Vite — Настройка
Заголовок раздела «Vite — Настройка»export default { build: { rollupOptions: { output: { manualChunks: { // Выносим тяжёлые библиотеки 'vendor-react': ['react', 'react-dom'], 'vendor-router': ['react-router-dom'], 'vendor-charts': ['recharts'], 'vendor-form': ['react-hook-form', 'zod'], }, }, }, },};
// Автоматически по директориямexport default { build: { rollupOptions: { output: { manualChunks(id) { if (id.includes('node_modules')) { return 'vendor'; } if (id.includes('src/pages')) { return 'pages'; } }, }, }, },};Next.js — Автоматический Code Splitting
Заголовок раздела «Next.js — Автоматический Code Splitting»Next.js автоматически делает code splitting по страницам:
// app/page.tsx — отдельный чанк// app/about/page.tsx — отдельный чанк// app/blog/[slug]/page.tsx — отдельный чанк
// ✅ Явная динамическая загрузкаimport dynamic from 'next/dynamic';
const HeavyComponent = dynamic(() => import('./HeavyComponent'), { loading: () => <p>Загрузка...</p>, ssr: false, // отключить SSR для этого компонента});
// ✅ Загрузка при viewportconst LazySection = dynamic(() => import('./LazySection'), { loading: () => <div style={{ height: 400 }}>Загрузка...</div>,});Анализ бандла
Заголовок раздела «Анализ бандла»# Webpack Bundle Analyzernpm install --save-dev webpack-bundle-analyzer
# В webpack.config.js:const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;plugins: [new BundleAnalyzerPlugin()]
# Vitenpm install --save-dev rollup-plugin-visualizer# vite.config.js:import { visualizer } from 'rollup-plugin-visualizer';plugins: [visualizer({ open: true })]
# Next.jsnpm install @next/bundle-analyzer# next.config.js:const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: true });module.exports = withBundleAnalyzer({});Prefetch — Загрузка заранее
Заголовок раздела «Prefetch — Загрузка заранее»// ✅ Prefetch при наведении (вероятный следующий переход)const links = document.querySelectorAll('a');links.forEach(link => { link.addEventListener('mouseenter', () => { const href = link.getAttribute('href'); if (href) { import(href); // начинаем загрузку } });});
// ✅ React Router prefetchimport { Link } from 'react-router-dom';
<Link to="/dashboard" onMouseEnter={() => import('./Dashboard')} // prefetch on hover> Dashboard</Link>