Lazy loading и code splitting

Понятие и необходимость

Lazy loading и code splitting — это подходы к оптимизации загрузки приложений, позволяющие уменьшить начальный размер бандла и ускорить рендеринг страниц. В больших приложениях полный код сразу загружать нецелесообразно: пользователю может понадобиться лишь часть функционала. Разделение кода на фрагменты и их динамическая подгрузка повышает производительность и отзывчивость интерфейса.

Key points:

  • Lazy loading — отложенная загрузка компонентов или ресурсов до момента их фактического использования.
  • Code splitting — деление исходного кода на отдельные чанки, которые могут подгружаться асинхронно.

Подключение Preact и настройка

Для работы с Preact рекомендуется использовать Preact CLI или настроенный Webpack/Rollup с поддержкой динамического импорта. Preact полностью совместим с механизмом import() для динамической загрузки модулей.

Пример базовой структуры проекта с Preact:

src/
  components/
    Header.js
    Footer.js
    Dashboard.js
  pages/
    Home.js
    About.js
index.js

Динамический импорт компонентов

Для реализации lazy loading используется функция lazy из пакета preact/compat. Она позволяет создавать компонент, который подгружается только при рендере:

import { h, Suspense } from 'preact';
import { lazy } from 'preact/compat';

const Dashboard = lazy(() => import('./components/Dashboard'));

function App() {
    return (
        <div>
            <h1>Приложение на Preact</h1>
            <Suspense fallback={<div>Загрузка...</div>}>
                <Dashboard />
            </Suspense>
        </div>
    );
}

export default App;

Разбор кода:

  • lazy(() => import(...)) создаёт компонент, который загружается только при рендере.
  • Suspense управляет состоянием загрузки, предоставляя возможность показать fallback-контент.

Использование с маршрутизацией

В Preact маршрутизация часто реализуется через preact-router. Lazy loading позволяет подгружать страницы только при переходе на соответствующий маршрут.

Пример интеграции:

import { h, Suspense } from 'preact';
import { lazy } from 'preact/compat';
import Router from 'preact-router';

const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));

function App() {
    return (
        <Suspense fallback={<div>Загрузка страницы...</div>}>
            <Router>
                <Home path="/" />
                <About path="/about" />
            </Router>
        </Suspense>
    );
}

export default App;

Особенности:

  • Каждая страница загружается отдельным чанком.
  • Переход между маршрутами не блокирует интерфейс.

Оптимизация загрузки

Для эффективного lazy loading важно учитывать несколько правил:

  1. Фрагментация логики: компоненты должны быть достаточно большими, чтобы оправдывать отдельный чанк, но не слишком крупными, чтобы задержка загрузки оставалась минимальной.
  2. Предварительная подгрузка: для популярных маршрутов можно использовать preload или prefetch, чтобы сократить задержку при переходе.
  3. Состояние загрузки: fallback-контент должен быть информативным и минимально нагружать интерфейс.

Пример предзагрузки:

const Dashboard = lazy(() => import('./components/Dashboard'));

if (window.location.pathname === '/dashboard') {
    import('./components/Dashboard');
}

Интеграция с Webpack/Rollup

Webpack автоматически создаёт отдельные чанки при использовании динамического импорта:

output: {
    filename: '[name].[contenthash].js',
    chunkFilename: '[name].[contenthash].js',
}

Rollup поддерживает code splitting через конфигурацию output:

output: {
    dir: 'dist',
    format: 'esm',
    entryFileNames: '[name].js',
    chunkFileNames: '[name]-[hash].js',
}

Особенности Preact

  • preact/compat обеспечивает совместимость с API React, включая lazy и Suspense.
  • Вес Preact значительно меньше React, что делает lazy loading ещё более эффективным, особенно на мобильных устройствах.
  • В сочетании с code splitting Preact позволяет уменьшить первоначальный бандл до минимально возможного размера, ускоряя Time to Interactive (TTI).

Примеры продвинутых сценариев

Lazy loading с условным рендером:

import { h, Suspense } from 'preact';
import { lazy } from 'preact/compat';

const AdminPanel = lazy(() => import('./components/AdminPanel'));

function App({ isAdmin }) {
    return (
        <div>
            <h1>Приложение</h1>
            {isAdmin && (
                <Suspense fallback={<div>Загрузка панели администратора...</div>}>
                    <AdminPanel />
                </Suspense>
            )}
        </div>
    );
}

Параллельная загрузка нескольких компонентов:

const Users = lazy(() => import('./components/Users'));
const Reports = lazy(() => import('./components/Reports'));

// В одной Suspense можно обернуть сразу несколько ленивых компонентов
<Suspense fallback={<div>Загрузка данных...</div>}>
    <Users />
    <Reports />
</Suspense>

Lazy loading и code splitting в Preact позволяют строить масштабируемые приложения с минимальным временем загрузки и высокой отзывчивостью интерфейса, оптимизируя использование ресурсов и ускоряя работу конечного пользователя.