Реалізація Lazy Loading компонентів на сайті

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація Lazy Loading компонентів на сайті
Середня
від 1 робочого дня до 3 робочих днів
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Реалізація Lazy Loading компонентів на сайті

Lazy loading — це відкладена завантаження компонентів, зображень або модулів до моменту, коли вони реально потрібні користувачу. Зменшує розмір початкового бандла, прискорює Time to Interactive та знижує витрату трафіку на мобільних пристроях.

Два рівні lazy loading

Рівень модулів — JavaScript-код компонента завантажується тільки при першому рендері. Реалізується через динамічний import().

Рівень ресурсів — зображення, iframe, відео завантажуються тільки коли елемент входить у область видимості. Реалізується через атрибут loading="lazy" або IntersectionObserver.

React.lazy і Suspense

// Без lazy loading — весь код у main bundle
import HeavyChart from '@/components/HeavyChart'
import DataTable from '@/components/DataTable'

// З lazy loading — окремі чанки, завантажуються по вимозі
import { lazy, Suspense } from 'react'

const HeavyChart = lazy(() => import('@/components/HeavyChart'))
const DataTable = lazy(() => import('@/components/DataTable'))

function Dashboard() {
  return (
    <div>
      <Suspense fallback={<ChartSkeleton />}>
        <HeavyChart data={chartData} />
      </Suspense>

      <Suspense fallback={<TableSkeleton />}>
        <DataTable rows={rows} />
      </Suspense>
    </div>
  )
}

Suspense перехоплює промис, який виконує lazy-компонент під час завантаження, і показує fallback. Як тільки чанк завантажено — рендерить компонент.

Іменовані експорти з lazy

React.lazy працює тільки з default export. Для іменованих потрібна обгортка:

// Якщо компонент експортується як named export
const BarChart = lazy(() =>
  import('@/components/charts').then(module => ({
    default: module.BarChart,
  }))
)

Умовна завантаження: тільки при видимості

Завантажувати важкий компонент одразу при монтуванні сторінки — не завжди потрібно. Якщо компонент знаходиться нижче fold, стоїть відкласти завантаження до прокрутки до нього:

// hooks/useLazyComponent.ts
import { useState, useEffect, useRef } from 'react'

export function useLazyComponent(threshold = '200px') {
  const ref = useRef<HTMLDivElement>(null)
  const [shouldRender, setShouldRender] = useState(false)

  useEffect(() => {
    const el = ref.current
    if (!el) return

    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setShouldRender(true)
          observer.disconnect()
        }
      },
      { rootMargin: threshold }
    )

    observer.observe(el)
    return () => observer.disconnect()
  }, [threshold])

  return { ref, shouldRender }
}
const HeavyMap = lazy(() => import('@/components/Map'))

function ContactPage() {
  const { ref, shouldRender } = useLazyComponent('400px')

  return (
    <div>
      <ContactForm />
      <div ref={ref} style={{ minHeight: 400 }}>
        {shouldRender ? (
          <Suspense fallback={<MapSkeleton />}>
            <HeavyMap lat={53.9} lng={27.5} />
          </Suspense>
        ) : (
          <MapSkeleton />
        )}
      </div>
    </div>
  )
}

Lazy loading зображень

// Нативний lazy loading — підтримується всіма сучасними браузерами
function ProductCard({ product }: { product: Product }) {
  return (
    <div>
      <img
        src={product.image}
        alt={product.title}
        loading="lazy"
        decoding="async"
        width={400}
        height={300}
      />
    </div>
  )
}

Для більш тонкого контролю — кастомний хук з IntersectionObserver:

function useLazyImage(src: string) {
  const imgRef = useRef<HTMLImageElement>(null)
  const [loaded, setLoaded] = useState(false)

  useEffect(() => {
    const img = imgRef.current
    if (!img) return

    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          img.src = src
          img.onload = () => setLoaded(true)
          observer.disconnect()
        }
      },
      { rootMargin: '100px' }
    )

    observer.observe(img)
    return () => observer.disconnect()
  }, [src])

  return { imgRef, loaded }
}

Next.js: динамічний імпорт

import dynamic from 'next/dynamic'

// З користувацьким loading state
const RichTextEditor = dynamic(
  () => import('@/components/RichTextEditor'),
  {
    loading: () => <EditorSkeleton />,
    ssr: false, // не рендерити на сервері — актуально для window-dependent компонентів
  }
)

// Тільки при умові
const AdminPanel = dynamic(() => import('@/components/AdminPanel'), { ssr: false })

function Page({ isAdmin }: { isAdmin: boolean }) {
  return isAdmin ? <AdminPanel /> : <UserView />
}

Vite: аналіз бандла

Щоб зрозуміти, що варто виносити в lazy chunks:

# Встановити плагін
npm install --save-dev rollup-plugin-visualizer

# vite.config.ts
import { visualizer } from 'rollup-plugin-visualizer'

export default {
  plugins: [
    visualizer({ open: true, gzipSize: true, filename: 'dist/stats.html' }),
  ],
}

Після npm run build відкриється інтерактивна карта бандла. Шукаємо крупні залежності: chart libraries, rich text editors, date pickers, map SDKs — перші кандидати на lazy loading.

Preload критичних чанків

Чанки, які майже гарантовано понадобляться, можна prefetch в idle time:

// Prefetch при hover на ссилку
function NavLink({ href, chunkImport, children }) {
  const handleMouseEnter = () => {
    chunkImport() // () => import('@/pages/About')
  }
  return <a href={href} onMouseEnter={handleMouseEnter}>{children}</a>
}

Строки виконання

Настройка React.lazy + Suspense для існуючих компонентів — 0.5 дня. Повний аудит бандла, приоритизація, реалізація intersection-based завантаження зі skeleton-заглушками — 2–3 дні залежно від кількості компонентів.