React.lazy() — это функция, которая позволяет динамически загружать компоненты React. Она реализует паттерн “ленивой загрузки” (lazy loading) и автоматически разделяет код приложения на отдельные бандлы (code splitting).
Основное использование:
// Вместо обычного импорта
import HeavyComponent from './HeavyComponent';
// Используем динамический импорт
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));
// Обязательно оборачиваем в Suspense
function App() {
return (
<React.Suspense fallback={<div>Загрузка...</div>}>
<HeavyComponent />
</React.Suspense>
);
}React.lazy() появился в React 16.6 и предоставляет встроенный способ для code splitting на уровне компонентов. Это позволяет загружать компоненты только тогда, когда они действительно нужны, уменьшая начальный размер бандла.
// React.lazy принимает функцию, которая возвращает Promise
const LazyComponent = React.lazy(() => import('./LazyComponent'));
// import() возвращает Promise, который резолвится в модуль
// Модуль должен экспортировать React компонент по умолчанию// LazyComponent.jsx
// ✅ Правильно: экспорт по умолчанию
export default function LazyComponent() {
return <div>Я загружаюсь лениво!</div>;
}
// ❌ Неправильно: именованный экспорт
export const LazyComponent = () => {
return <div>Это не будет работать</div>;
};
// Обходной путь для именованных экспортов
const LazyComponent = React.lazy(() =>
import('./components').then(module => ({
default: module.NamedComponent
}))
);function App() {
return (
<div>
{/* ❌ Ошибка: LazyComponent должен быть внутри Suspense */}
<LazyComponent />
{/* ✅ Правильно: используем Suspense */}
<React.Suspense fallback={<div>Загрузка...</div>}>
<LazyComponent />
</React.Suspense>
</div>
);
}// Можно оборачивать несколько lazy компонентов
function Dashboard() {
return (
<React.Suspense fallback={<DashboardSkeleton />}>
<UserProfile />
<Analytics />
<RecentActivity />
</React.Suspense>
);
}
// Вложенные Suspense для разной гранулярности
function App() {
return (
<React.Suspense fallback={<AppLoader />}>
<Header />
<React.Suspense fallback={<ContentLoader />}>
<MainContent />
</React.Suspense>
<Footer />
</React.Suspense>
);
}import { BrowserRouter, Routes, Route } from 'react-router-dom';
// Ленивая загрузка страниц
const Home = React.lazy(() => import('./pages/Home'));
const About = React.lazy(() => import('./pages/About'));
const Dashboard = React.lazy(() => import('./pages/Dashboard'));
const Profile = React.lazy(() => import('./pages/Profile'));
function App() {
return (
<BrowserRouter>
<React.Suspense fallback={<PageLoader />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/profile/:id" element={<Profile />} />
</Routes>
</React.Suspense>
</BrowserRouter>
);
}const AdminPanel = React.lazy(() => import('./AdminPanel'));
function App() {
const [user, setUser] = useState(null);
return (
<div>
{user?.isAdmin && (
<React.Suspense fallback={<div>Загрузка панели...</div>}>
<AdminPanel user={user} />
</React.Suspense>
)}
</div>
);
}// Компонент с тяжёлой библиотекой для графиков
const ChartComponent = React.lazy(() => import('./ChartComponent'));
function Analytics() {
const [showChart, setShowChart] = useState(false);
return (
<div>
<button onClick={() => setShowChart(true)}>
Показать график
</button>
{showChart && (
<React.Suspense fallback={<ChartSkeleton />}>
<ChartComponent data={analyticsData} />
</React.Suspense>
)}
</div>
);
}class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error('Ошибка загрузки компонента:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return <div>Не удалось загрузить компонент</div>;
}
return this.props.children;
}
}
// Использование
function App() {
return (
<ErrorBoundary>
<React.Suspense fallback={<Loader />}>
<LazyComponent />
</React.Suspense>
</ErrorBoundary>
);
}function LazyComponentWithRetry(componentPath) {
return React.lazy(() =>
import(componentPath).catch(() => {
// Перезагрузка страницы при ошибке загрузки чанка
window.location.reload();
// Или повторная попытка
return new Promise((resolve) => {
setTimeout(() => {
resolve(import(componentPath));
}, 1500);
});
})
);
}
const MyComponent = LazyComponentWithRetry('./MyComponent');// Функция для предзагрузки
const preloadComponent = (component) => {
component._init();
};
// Создаём lazy компонент
const HeavyModal = React.lazy(() => import('./HeavyModal'));
// Предзагружаем при наведении
function App() {
const [showModal, setShowModal] = useState(false);
return (
<div>
<button
onMouseEnter={() => preloadComponent(HeavyModal)}
onClick={() => setShowModal(true)}
>
Открыть модалку
</button>
{showModal && (
<React.Suspense fallback={<ModalLoader />}>
<HeavyModal onClose={() => setShowModal(false)} />
</React.Suspense>
)}
</div>
);
}// Компонент с прогресс-баром
function ProgressiveSuspense({ children }) {
const [progress, setProgress] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setProgress(prev => Math.min(prev + 10, 90));
}, 100);
return () => clearInterval(timer);
}, []);
return (
<React.Suspense
fallback={
<div>
<div>Загрузка... {progress}%</div>
<ProgressBar value={progress} />
</div>
}
>
{children}
</React.Suspense>
);
}// Функция для динамической загрузки локалей
const loadLocale = (locale) => {
return React.lazy(() =>
import(`./locales/${locale}.js`).then(module => ({
default: module.LocaleProvider
}))
);
};
function App() {
const [locale, setLocale] = useState('ru');
const LocaleProvider = useMemo(() => loadLocale(locale), [locale]);
return (
<React.Suspense fallback={<div>Загрузка языка...</div>}>
<LocaleProvider>
<MainApp />
</LocaleProvider>
</React.Suspense>
);
}// Вместо множества мелких lazy импортов
const Button = React.lazy(() => import('./Button'));
const Input = React.lazy(() => import('./Input'));
const Select = React.lazy(() => import('./Select'));
// Лучше сгруппировать в один модуль
const FormComponents = React.lazy(() => import('./FormComponents'));
// FormComponents экспортирует { Button, Input, Select }// По функциональности
const AdminFeatures = React.lazy(() => import('./features/admin'));
const UserFeatures = React.lazy(() => import('./features/user'));
// По размеру бандла
const LargeTable = React.lazy(() =>
import(/* webpackChunkName: "large-table" */ './LargeTable')
);
// По приоритету загрузки
const CriticalComponent = React.lazy(() =>
import(/* webpackPreload: true */ './CriticalComponent')
);
const OptionalComponent = React.lazy(() =>
import(/* webpackPrefetch: true */ './OptionalComponent')
);// ❌ Не работает напрямую с именованными экспортами
const { Component } = React.lazy(() => import('./module'));
// ✅ Нужно преобразовать
const Component = React.lazy(() =>
import('./module').then(module => ({
default: module.Component
}))
);// Для SSR используйте loadable-components или похожие решения
import loadable from '@loadable/component';
const LazyComponent = loadable(() => import('./Component'));// ❌ Забыли Suspense = ошибка
<LazyComponent />
// ✅ Всегда оборачивайте в Suspense
<React.Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</React.Suspense>React.lazy() — мощный инструмент для оптимизации производительности React-приложений, который при правильном использовании значительно улучшает время начальной загрузки.
Хотите больше статей для подготовки к собеседованиям? Подписывайтесь на EasyAdvice, добавляйте сайт в закладки и совершенствуйтесь каждый день 💪