← Назад к вопросам

Как использовал Lazy Loading в React?

2.0 Middle🔥 291 комментариев
#React#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Lazy Loading в React

Lazy Loading — это техника оптимизации, которая откладывает загрузку компонентов, изображений и других ресурсов до момента, когда они действительно нужны. В контексте React это означает загрузку кода компонента только когда его нужно отрендерить.

React.lazy и Suspense

Основной способ ленивой загрузки компонентов в React — использование React.lazy() с Suspense:

import React, { Suspense, lazy } from 'react';

// Обычный способ импорта (весь код загружается сразу)
// import HeavyComponent from './HeavyComponent';

// Ленивая загрузка (код загружается при первой необходимости)
const HeavyComponent = lazy(() => import('./HeavyComponent'));
const Admin = lazy(() => import('./pages/Admin'));
const Dashboard = lazy(() => import('./pages/Dashboard'));

function App() {
  return (
    <Suspense fallback={<LoadingSpinner />}>
      <HeavyComponent />
    </Suspense>
  );
}

Полный пример с маршрутизацией

import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { Suspense, lazy } from 'react';

// Ленивая загрузка страниц
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Contact = lazy(() => import('./pages/Contact'));
const Admin = lazy(() => import('./pages/Admin'));

function LoadingFallback() {
  return <div className="flex items-center justify-center p-8">Loading...</div>;
}

function App() {
  return (
    <BrowserRouter>
      <Suspense fallback={<LoadingFallback />}>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
          <Route path="/contact" element={<Contact />} />
          <Route path="/admin" element={<Admin />} />
        </Routes>
      </Suspense>
    </BrowserRouter>
  );
}

export default App;

Ленивая загрузка с Next.js

В Next.js есть встроенная функция dynamic для ленивой загрузки:

import dynamic from 'next/dynamic';

// Базовая ленивая загрузка
const HeavyChart = dynamic(() => import('../components/Chart'), {
  loading: () => <p>Loading chart...</p>,
  ssr: false // Отключить SSR для этого компонента
});

// С именованным экспортом
const Editor = dynamic(
  () => import('../components/Editor').then(mod => mod.Editor),
  { loading: () => <p>Loading editor...</p> }
);

export default function Page() {
  return <HeavyChart />;
}

Ленивая загрузка изображений

// HTML нативный способ
<img
  src="image.jpg"
  loading="lazy"
  alt="Description"
/>

// В Next.js
import Image from 'next/image';

function Gallery() {
  return (
    <Image
      src="/image.jpg"
      alt="Description"
      width={800}
      height={600}
      loading="lazy"
    />
  );
}

Intersection Observer API для ленивой загрузки

Для более сложных случаев используй Intersection Observer:

import { useEffect, useRef, useState } from 'react';

function LazyImage({ src, alt }) {
  const [isVisible, setIsVisible] = useState(false);
  const imgRef = useRef(null);

  useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setIsVisible(true);
          observer.unobserve(entry.target);
        }
      },
      { threshold: 0.1 }
    );

    if (imgRef.current) {
      observer.observe(imgRef.current);
    }

    return () => observer.disconnect();
  }, []);

  return (
    <img
      ref={imgRef}
      src={isVisible ? src : 'placeholder.jpg'}
      alt={alt}
      className="w-full h-auto"
    />
  );
}

Кастомный хук для ленивой загрузки

import { useState, useEffect, useRef } from 'react';

function useLazyLoad() {
  const [isVisible, setIsVisible] = useState(false);
  const ref = useRef(null);

  useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setIsVisible(true);
          observer.unobserve(ref.current);
        }
      },
      { threshold: 0.1, rootMargin: '50px' }
    );

    if (ref.current) {
      observer.observe(ref.current);
    }

    return () => {
      if (ref.current) observer.unobserve(ref.current);
    };
  }, []);

  return { ref, isVisible };
}

// Использование
function ExpensiveComponent() {
  const { ref, isVisible } = useLazyLoad();

  return (
    <div ref={ref}>
      {isVisible ? <HeavyComponent /> : <Skeleton />}
    </div>
  );
}

Код-сплиттинг (Code Splitting)

Lazy loading неразрывно связан с code splitting — разделением бандла на части:

// webpack.config.js может быть таким
const path = require('path');

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[contenthash].js',
    chunkFilename: '[name].[contenthash].chunk.js'
  },
  optimization: {
    splitChunks: {
      chunks: 'all',
      minSize: 20000,
      minChunks: 1,
      maxAsyncRequests: 30,
      maxInitialRequests: 30,
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          priority: 10
        }
      }
    }
  }
};

Практические примеры использования

1. Модальное окно

const Modal = lazy(() => import('./Modal'));

function Page() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <div>
      <button onClick={() => setIsOpen(true)}>Open Modal</button>
      {isOpen && (
        <Suspense fallback={<p>Loading...</p>}>
          <Modal onClose={() => setIsOpen(false)} />
        </Suspense>
      )}
    </div>
  );
}

2. Табы с ленивой загрузкой

const Tab1 = lazy(() => import('./tabs/Tab1'));
const Tab2 = lazy(() => import('./tabs/Tab2'));
const Tab3 = lazy(() => import('./tabs/Tab3'));

function Tabs() {
  const [activeTab, setActiveTab] = useState(0);

  const tabs = [Tab1, Tab2, Tab3];
  const ActiveTab = tabs[activeTab];

  return (
    <div>
      <div className="flex gap-2">
        {['Tab 1', 'Tab 2', 'Tab 3'].map((name, i) => (
          <button
            key={i}
            onClick={() => setActiveTab(i)}
            className={activeTab === i ? 'font-bold' : ''}
          >
            {name}
          </button>
        ))}
      </div>
      <Suspense fallback={<p>Loading...</p>}>
        <ActiveTab />
      </Suspense>
    </div>
  );
}

3. Бесконечный скролл

function InfiniteScroll() {
  const [items, setItems] = useState([]);
  const [page, setPage] = useState(1);
  const { ref, isVisible } = useLazyLoad();

  useEffect(() => {
    if (isVisible) {
      fetchMoreItems(page).then(newItems => {
        setItems(prev => [...prev, ...newItems]);
        setPage(p => p + 1);
      });
    }
  }, [isVisible, page]);

  return (
    <div>
      {items.map(item => <Item key={item.id} {...item} />)}
      <div ref={ref} className="p-4 text-center">Loading more...</div>
    </div>
  );
}

Преимущества Lazy Loading

  • Быстрая загрузка страницы — меньше кода загружается изначально
  • Улучшенная производительность — компоненты загружаются по мере необходимости
  • Экономия трафика — пользователи не загружают код для неиспользуемых частей
  • Лучше для SEO — страница загружается быстрее
  • Масштабируемость — можешь добавлять новые компоненты без увеличения главного бандла

Когда использовать

  • Маршруты — всегда используй lazy loading для страниц в SPA
  • Модальные окна — загружай только когда открываются
  • Табы — загружай содержимое при клике
  • Тяжёлые библиотеки — графики, редакторы, карты
  • Изображения — особенно ниже fold

Заключение

Lazy Loading в React достигается через React.lazy и Suspense для компонентов, dynamic в Next.js, и Intersection Observer API для изображений и содержимого. Это критична оптимизация для быстрой загрузки приложения и хорошего пользовательского опыта. Используй lazy loading для маршрутов, модальных окон и тяжёлых компонентов.