В современных приложениях на React часто возникает необходимость
разделять код на части, чтобы оптимизировать загрузку страниц и снизить
время первого рендера. Для этих целей React Router предоставляет функцию
lazy, которая позволяет динамически загружать компоненты
маршрутов только по необходимости.
lazyФункция lazy используется для ленивой загрузки
компонентов маршрутов. Она интегрируется с динамическим импортом из
ES-модулей:
import { lazy } from "react";
import { createBrowserRouter } from "react-router-dom";
const Home = lazy(() => import("./pages/Home"));
const About = lazy(() => import("./pages/About"));
const router = createBrowserRouter([
{ path: "/", element: <Home /> },
{ path: "/about", element: <About /> },
]);
Здесь lazy(() => import("./pages/Home")) создаёт
компонент, который будет загружен только при необходимости. На этапе
сборки Webpack формирует отдельные чанки для этих компонентов, что
снижает размер основного бандла.
SuspenseТак как lazy возвращает компонент, загрузка которого
асинхронна, необходимо обернуть маршруты или отдельные ленивые
компоненты в <Suspense>:
import { Suspense } from "react";
import { RouterProvider } from "react-router-dom";
<Suspense fallback={<div>Загрузка...</div>}>
<RouterProvider router={router} />
</Suspense>
fallback отображается до того момента, пока ленивый
компонент полностью не загрузится. Это может быть простой индикатор
загрузки, спиннер или любая другая UI-компонента.
React Router поддерживает ленивые компоненты в любой части дерева маршрутов, включая вложенные маршруты:
const Dashboard = lazy(() => import("./pages/Dashboard"));
const Settings = lazy(() => import("./pages/Settings"));
const router = createBrowserRouter([
{
path: "/dashboard",
element: <Dashboard />,
children: [
{ path: "settings", element: <Settings /> },
],
},
]);
Такой подход позволяет загружать каждый уровень маршрутов отдельно, уменьшая начальный объём кода и ускоряя время отклика.
При использовании ленивых компонентов возможны ошибки загрузки,
например из-за сетевых проблем. Для их обработки можно использовать
ErrorBoundary:
import { lazy } from "react";
import { Suspense } from "react";
const Profile = lazy(() => import("./pages/Profile"));
function ProfileWrapper() {
return (
<ErrorBoundary fallback={<div>Ошибка загрузки профиля</div>}>
<Suspense fallback={<div>Загрузка профиля...</div>}>
<Profile />
</Suspense>
</ErrorBoundary>
);
}
ErrorBoundary гарантирует, что сбой в ленивой загрузке
не приведёт к падению всего приложения.
React Router версии 6.4+ предоставляет возможность лениво загружать
данные через loader. Можно комбинировать ленивые компоненты
с loader, чтобы минимизировать задержки:
const Products = lazy(() => import("./pages/Products"));
const router = createBrowserRouter([
{
path: "/products",
element: <Products />,
loader: async () => {
const response = await fetch("/api/products");
return response.json();
},
},
]);
Таким образом, компонент и данные для него загружаются только при переходе на маршрут.
import { lazy, Suspense } from "react";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
const Home = lazy(() => import("./pages/Home"));
const About = lazy(() => import("./pages/About"));
const Dashboard = lazy(() => import("./pages/Dashboard"));
const router = createBrowserRouter([
{ path: "/", element: <Home /> },
{ path: "/about", element: <About /> },
{
path: "/dashboard",
element: <Dashboard />,
children: [
{ path: "settings", element: lazy(() => import("./pages/Settings")) },
],
},
]);
<Suspense fallback={<div>Загрузка приложения...</div>}>
<RouterProvider router={router} />
</Suspense>
В этом примере реализована полная ленивость: каждая страница загружается отдельно, а вложенные маршруты тоже обрабатываются асинхронно, что обеспечивает высокую производительность и гибкость.