Utilizziamo i cookie per migliorare la tua esperienza sul sito
CodeWorlds

Strategie ładowania obrazów (lazy, eager, priority) w Next.js 15

W Metropolis Quantum, system transportowy miasta jest zorganizowany według różnych priorytetów. Pojazdy ratunkowe mają najwyższy priorytet i zawsze mają pierwszeństwo przejazdu. Transport publiczny ma średni priorytet, a pojazdy prywatne najniższy. To inteligentne zarządzanie ruchem zapewnia, że najważniejsze zasoby zawsze docierają do celu najszybciej, podczas gdy mniej krytyczne mogą poczekać.

Podobnie w aplikacjach internetowych, różne obrazy mają różne poziomy ważności. Obraz główny (hero image) strony powinien załadować się natychmiast, podczas gdy obrazy umieszczone niżej na stronie mogą być ładowane tylko wtedy, gdy użytkownik przewinie do nich. Next.js 15 oferuje różne strategie ładowania obrazów, które pozwalają dostosować sposób ładowania obrazów do ich ważności i miejsca na stronie.

Trzy główne strategie ładowania obrazów

W Next.js 15 mamy do dyspozycji trzy główne strategie ładowania obrazów:

  1. Lazy (leniwe) - domyślna strategia, obrazy są ładowane tylko wtedy, gdy zbliżają się do widocznego obszaru (viewport)
  2. Eager (gorliwe) - obrazy są ładowane natychmiast, niezależnie od ich pozycji na stronie
  3. Priority (priorytetowe) - obrazy są ładowane z najwyższym priorytetem, idealnie dla obrazów "above the fold"

Każda z tych strategii ma swoje zastosowanie i wpływ na wydajność strony.

Lazy Loading (leniwe ładowanie)

Leniwe ładowanie to domyślna strategia dla komponentu

Image
w Next.js 15. Działa ona zgodnie z prostą zasadą:

Ładuj tylko to, co użytkownik aktualnie widzi lub wkrótce zobaczy.

Jak działa lazy loading?

  1. Next.js automatycznie opóźnia ładowanie obrazów, które znajdują się poza widocznym obszarem (viewport)
  2. Obrazy są ładowane dopiero wtedy, gdy użytkownik przewinie stronę i zbliżą się do widocznego obszaru
  3. W tle używana jest nowoczesna funkcja przeglądarki
    loading="lazy"
    , z dodatkowymi optymalizacjami

Przykład użycia lazy loading:

1import Image from 'next/image';
2
3export default function QuantumGallery() {
4  return (
5    <div className="gallery">
6      {Array.from({ length: 20 }).map((_, index) => (
7        <div key={index} className="relative h-64 w-full">
8          <Image
9            src={`/images/quantum-city-${index + 1}.jpg`}
10            alt={`Metropolis Quantum - zdjęcie ${index + 1}`}
11            fill
12            style={{ objectFit: 'cover' }}
13            // Nie trzeba dodawać loading="lazy" - jest to wartość domyślna
14          />
15        </div>
16      ))}
17    </div>
18  );
19}

Zalety lazy loading:

  1. Szybsze wstępne ładowanie strony - tylko krytyczne obrazy są ładowane natychmiast
  2. Mniejsze zużycie danych - obrazy, do których użytkownik nigdy nie dotrze, nie są ładowane
  3. Lepsza wydajność - przeglądarka może skupić zasoby na elementach, które są aktualnie ważne

Scenariusze, w których warto używać lazy loading:

  • Długie strony z wieloma obrazami
  • Galerie zdjęć
  • Listy produktów
  • Karuzele obrazów
  • Strony z nieskończonym przewijaniem

Eager Loading (gorliwe ładowanie)

Strategia eager loading jest przeciwieństwem leniwego ładowania. Zamiast czekać, aż obrazy zbliżą się do widocznego obszaru, obrazy są ładowane natychmiast podczas ładowania strony.

Jak ustawić eager loading:

1import Image from 'next/image';
2
3export default function QuantumLogo() {
4  return (
5    <Image
6      src="/images/quantum-logo.png"
7      alt="Logo Metropolis Quantum"
8      width={200}
9      height={100}
10      loading="eager"
11    />
12  );
13}

Zalety eager loading:

  1. Natychmiastowa dostępność - wszystkie obrazy są dostępne od razu po załadowaniu strony
  2. Brak opóźnień - użytkownik nie musi czekać na ładowanie obrazów podczas przewijania
  3. Bardziej przewidywalne zachowanie - wszystkie zasoby są ładowane w jednym cyklu

Scenariusze, w których warto używać eager loading:

  • Krótkie strony z niewielką liczbą obrazów
  • Strony, gdzie wszystkie obrazy są ważne dla kontekstu
  • Aplikacje, gdzie płynne przewijanie bez opóźnień jest kluczowe
  • Aplikacje offline-first, które chcą załadować wszystkie zasoby od razu

Priority Loading (priorytetowe ładowanie)

Priority loading to specjalna strategia dla najważniejszych obrazów na stronie, szczególnie tych widocznych "above the fold" (w górnej części strony, widocznej bez przewijania).

Jak ustawić priority loading:

1import Image from 'next/image';
2
3export default function QuantumHero() {
4  return (
5    <Image
6      src="/images/quantum-hero.jpg"
7      alt="Witaj w Metropolis Quantum"
8      width={1920}
9      height={1080}
10      priority
11    />
12  );
13}

Gdy używasz atrybutu

priority
:

  1. Next.js automatycznie ustawia
    loading="eager"
    dla danego obrazu
  2. Dodaje specjalny znacznik
    <link rel="preload">
    dla tego obrazu
  3. Obraz otrzymuje wyższy priorytet w kolejce ładowania przeglądarki

Zalety priority loading:

  1. Niższe LCP (Largest Contentful Paint) - kluczowa metryka Core Web Vitals
  2. Szybsze wyświetlanie najważniejszych obrazów - lepsza percepcja wydajności
  3. Optymalizacja dla Core Web Vitals - pozytywny wpływ na SEO

Scenariusze, w których warto używać priority:

  • Główne obrazy (hero images) stron
  • Logo marki
  • Obrazy produktów na stronach szczegółowych
  • Awatary użytkowników w aplikacjach społecznościowych
  • Wszelkie obrazy, które stanowią główną zawartość strony

Kluczowe uwagi dotyczące atrybutu priority:

  • Używaj
    priority
    oszczędnie - tylko dla najważniejszych obrazów
  • Google zaleca używanie
    priority
    tylko dla obrazów LCP (Largest Contentful Paint)
  • Zbyt wiele obrazów z
    priority
    może paradoksalnie zmniejszyć wydajność

Porównanie strategii ładowania

| Strategia | Atrybut | Przypadki użycia | Wpływ na wydajność | |-----------|---------|------------------|-------------------| | Lazy | loading="lazy" (domyślne) | Obrazy poniżej "fold", galerie, listy | Szybsze ładowanie początkowe, oszczędność danych | | Eager | loading="eager" | Małe strony, wszystkie obrazy ważne | Wolniejsze ładowanie początkowe, lepsza responsywność podczas przewijania | | Priority | priority | Obrazy "above the fold", hero images, główna zawartość | Lepsze LCP, szybsze wyświetlanie kluczowych obrazów |

Zaawansowane strategie ładowania obrazów

Poza podstawowymi strategiami, Next.js 15 pozwala na bardziej zaawansowane podejścia do ładowania obrazów:

1. Preload ważnych obrazów z useRouter

Czasami chcemy wstępnie załadować obrazy dla strony, na którą użytkownik prawdopodobnie przejdzie:

1'use client';
2
3import { useEffect } from 'react';
4import { useRouter } from 'next/navigation';
5import Image from 'next/image';
6
7export default function ProductCard({ product, isPriority }) {
8  const router = useRouter();
9  
10  useEffect(() => {
11    // Preload obrazu szczegółowego produktu po najechaniu myszą
12    const preloadDetailImage = () => {
13      router.prefetch(`/products/${product.id}`);
14    };
15    
16    const cardElement = document.getElementById(`product-card-${product.id}`);
17    if (cardElement) {
18      cardElement.addEventListener('mouseenter', preloadDetailImage);
19      return () => {
20        cardElement.removeEventListener('mouseenter', preloadDetailImage);
21      };
22    }
23  }, [product.id, router]);
24  
25  return (
26    <div 
27      id={`product-card-${product.id}`}
28      className="relative h-64 overflow-hidden rounded-lg"
29      onClick={() => router.push(`/products/${product.id}`)}
30    >
31      <Image
32        src={product.thumbnail}
33        alt={product.title}
34        fill
35        style={{ objectFit: 'cover' }}
36        priority={isPriority}
37      />
38      <div className="absolute bottom-0 w-full bg-black bg-opacity-60 p-4">
39        <h3 className="text-white font-bold">{product.title}</h3>
40      </div>
41    </div>
42  );
43}

2. Dynamiczne przełączanie priority w zależności od widoczności

Możemy dynamicznie nadawać priorytet obrazom, które są aktualnie widoczne:

1'use client';
2
3import { useState, useEffect, useRef } from 'react';
4import Image from 'next/image';
5
6export default function DynamicPriorityImage({ src, alt }) {
7  const [isPriority, setIsPriority] = useState(false);
8  const imageRef = useRef(null);
9  
10  useEffect(() => {
11    const observer = new IntersectionObserver(
12      ([entry]) => {
13        if (entry.isIntersecting) {
14          setIsPriority(true);
15          observer.disconnect();
16        }
17      },
18      { threshold: 0.1 }
19    );
20    
21    if (imageRef.current) {
22      observer.observe(imageRef.current);
23    }
24    
25    return () => {
26      observer.disconnect();
27    };
28  }, []);
29  
30  return (
31    <div ref={imageRef} className="relative h-64 w-full">
32      <Image
33        src={src}
34        alt={alt}
35        fill
36        style={{ objectFit: 'cover' }}
37        priority={isPriority}
38      />
39    </div>
40  );
41}

3. Progresywne ładowanie obrazów

Można łączyć strategie, aby najpierw wyświetlić obraz niskiej jakości, a następnie zastąpić go wersją wysokiej jakości:

1'use client';
2
3import { useState } from 'react';
4import Image from 'next/image';
5
6export default function ProgressiveImage() {
7  const [isLoaded, setIsLoaded] = useState(false);
8
9  return (
10    <div className="relative h-96 w-full">
11      {/* Niskokwalitywowy placeholder */}
12      <Image
13        src="/images/quantum-city-low.jpg"
14        alt="Metropolis Quantum"
15        fill
16        style={{
17          objectFit: 'cover',
18          opacity: isLoaded ? 0 : 1,
19          transition: 'opacity 0.5s ease-in-out'
20        }}
21        priority // Placeholder ładowany natychmiast
22      />
23      
24      {/* Obraz wysokiej jakości */}
25      <Image
26        src="/images/quantum-city-high.jpg"
27        alt="Metropolis Quantum"
28        fill
29        style={{ 
30          objectFit: 'cover',
31          opacity: isLoaded ? 1 : 0,
32          transition: 'opacity 0.5s ease-in-out'
33        }}
34        onLoadingComplete={() => setIsLoaded(true)}
35      />
36    </div>
37  );
38}

Optymalizacja obrazów dla różnych urządzeń

Strategie ładowania można łączyć z technikami dostosowywania obrazów do różnych urządzeń:

1. Responsywne obrazy z different źródłami

1import Image from 'next/image';
2
3export default function ResponsiveHero() {
4  return (
5    <>
6      {/* Obraz dla urządzeń mobilnych */}
7      <div className="md:hidden">
8        <Image
9          src="/images/hero-mobile.jpg"
10          alt="Metropolis Quantum"
11          width={640}
12          height={960}
13          priority // Priorytet dla głównego obrazu
14        />
15      </div>
16      
17      {/* Obraz dla tabletów */}
18      <div className="hidden md:block lg:hidden">
19        <Image
20          src="/images/hero-tablet.jpg"
21          alt="Metropolis Quantum"
22          width={1024}
23          height={768}
24          priority
25        />
26      </div>
27      
28      {/* Obraz dla desktopów */}
29      <div className="hidden lg:block">
30        <Image
31          src="/images/hero-desktop.jpg"
32          alt="Metropolis Quantum"
33          width={1920}
34          height={1080}
35          priority
36        />
37      </div>
38    </>
39  );
40}

2. Adaptacyjne ładowanie w zależności od łącza

1'use client';
2
3import { useState, useEffect } from 'react';
4import Image from 'next/image';
5
6export default function AdaptiveLoadingImage() {
7  const [connectionType, setConnectionType] = useState('high');
8  
9  useEffect(() => {
10    // Wykryj jakość połączenia
11    const connection = navigator.connection || 
12                      navigator.mozConnection || 
13                      navigator.webkitConnection;
14    
15    if (connection) {
16      if (connection.effectiveType === '4g' && !connection.saveData) {
17        setConnectionType('high');
18      } else {
19        setConnectionType('low');
20      }
21      
22      // Nasłuchuj zmian w jakości połączenia
23      const updateConnectionStatus = () => {
24        if (connection.effectiveType === '4g' && !connection.saveData) {
25          setConnectionType('high');
26        } else {
27          setConnectionType('low');
28        }
29      };
30      
31      connection.addEventListener('change', updateConnectionStatus);
32      return () => connection.removeEventListener('change', updateConnectionStatus);
33    }
34  }, []);
35  
36  return (
37    <div className="relative h-64 w-full">
38      <Image
39        src={connectionType === 'high' ? '/images/high-quality.jpg' : '/images/low-quality.jpg'}
40        alt="Adaptacyjny obraz"
41        fill
42        style={{ objectFit: 'cover' }}
43        loading={connectionType === 'high' ? 'eager' : 'lazy'}
44        quality={connectionType === 'high' ? 90 : 50}
45      />
46    </div>
47  );
48}

Implementacja zaawansowanych strategii ładowania w realnej aplikacji

Przykład: Strona produktowa z różnymi strategiami ładowania

1// app/products/[id]/page.tsx
2import Image from 'next/image';
3import { getProduct, getRelatedProducts } from '@/lib/api';
4import RelatedProducts from '@/components/RelatedProducts';
5
6export default async function ProductPage({ params }) {
7  const product = await getProduct((await params).id);
8  const relatedProducts = await getRelatedProducts((await params).id);
9  
10  return (
11    <div className="container mx-auto px-4 py-8">
12      <div className="grid grid-cols-1 md:grid-cols-2 gap-8">
13        {/* Główny obraz produktu - priority loading */}
14        <div className="relative aspect-square">
15          <Image
16            src={product.mainImage}
17            alt={product.name}
18            fill
19            style={{ objectFit: 'contain' }}
20            priority
21            sizes="(max-width: 768px) 100vw, 50vw"
22          />
23        </div>
24        
25        <div>
26          <h1 className="text-3xl font-bold mb-4">{product.name}</h1>
27          <p className="text-xl mb-4">{product.price}</p>
28          <div className="prose max-w-none">
29            <p>{product.description}</p>
30          </div>
31          
32          <button className="mt-6 bg-blue-600 text-white px-6 py-3 rounded-md">
33            Dodaj do koszyka
34          </button>
35        </div>
36      </div>
37      
38      {/* Galeria dodatkowych zdjęć - lazy loading (domyślne) */}
39      <div className="mt-12">
40        <h2 className="text-2xl font-bold mb-6">Galeria produktu</h2>
41        <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
42          {product.gallery.map((image, index) => (
43            <div key={index} className="relative aspect-square">
44              <Image
45                src={image.url}
46                alt={`${product.name} - zdjęcie ${index + 1}`}
47                fill
48                style={{ objectFit: 'cover' }}
49                sizes="(max-width: 768px) 50vw, 25vw"
50                // Domyślne lazy loading
51              />
52            </div>
53          ))}
54        </div>
55      </div>
56      
57      {/* Podobne produkty - eager loading dla pierwszego elementu */}
58      <div className="mt-12">
59        <h2 className="text-2xl font-bold mb-6">Podobne produkty</h2>
60        <RelatedProducts products={relatedProducts} />
61      </div>
62    </div>
63  );
64}
1// components/RelatedProducts.tsx
2'use client';
3
4import Image from 'next/image';
5import Link from 'next/link';
6import { useRef, useEffect } from 'react';
7
8export default function RelatedProducts({ products }) {
9  const containerRef = useRef(null);
10  
11  useEffect(() => {
12    // Preload images when hovering over the section
13    const preloadImages = () => {
14      products.forEach(product => {
15        const img = new Image();
16        img.src = product.image;
17      });
18    };
19    
20    const container = containerRef.current;
21    if (container) {
22      container.addEventListener('mouseenter', preloadImages, { once: true });
23      return () => {
24        container.removeEventListener('mouseenter', preloadImages);
25      };
26    }
27  }, [products]);
28  
29  return (
30    <div ref={containerRef} className="grid grid-cols-2 md:grid-cols-4 gap-4">
31      {products.map((product, index) => (
32        <Link key={product.id} href={`/products/${product.id}`}>
33          <div className="relative aspect-square rounded-lg overflow-hidden">
34            <Image
35              src={product.image}
36              alt={product.name}
37              fill
38              style={{ objectFit: 'cover' }}
39              // Eager loading tylko dla pierwszego produktu
40              loading={index === 0 ? 'eager' : 'lazy'}
41              sizes="(max-width: 768px) 50vw, 25vw"
42            />
43            <div className="absolute bottom-0 w-full bg-black bg-opacity-60 p-2">
44              <p className="text-white text-sm font-medium truncate">
45                {product.name}
46              </p>
47            </div>
48          </div>
49        </Link>
50      ))}
51    </div>
52  );
53}

Wpływ strategii ładowania na Core Web Vitals

Core Web Vitals to zestaw metryk Google, które mierzą doświadczenie użytkownika na stronach internetowych. Wybór właściwej strategii ładowania obrazów ma bezpośredni wpływ na te metryki:

1. LCP (Largest Contentful Paint)

LCP mierzy czas ładowania największego elementu widocznego w viewport, którym często jest obraz.

Wpływ strategii ładowania:

  • Priority loading dla głównego obrazu może znacząco poprawić LCP
  • Eager loading dla wszystkich obrazów może pogorszyć LCP przez rozproszenie zasobów
  • Lazy loading dla obrazów poniżej "fold" nie wpływa na LCP, ale poprawia ogólną wydajność

2. CLS (Cumulative Layout Shift)

CLS mierzy niestabilność układu strony podczas ładowania.

Wpływ strategii ładowania:

  • Wszystkie strategie mogą minimalizować CLS, jeśli obrazy mają określone wymiary
  • Priority loading z
    placeholder="blur"
    zapewnia najlepsze CLS, ponieważ rezerwuje przestrzeń natychmiast
  • Zawsze określaj
    width
    i
    height
    lub używaj
    fill
    z kontenerem o określonych wymiarach

3. FID (First Input Delay)

FID mierzy czas, jaki mija od pierwszej interakcji użytkownika do momentu, gdy przeglądarka może na nią zareagować.

Wpływ strategii ładowania:

  • Lazy loading może poprawić FID przez zmniejszenie pracy przeglądarki podczas ładowania
  • Eager loading może pogorszyć FID przez zwiększenie obciążenia głównego wątku
  • Priority loading dla kluczowych obrazów może być kompromisem

Pomiary i optymalizacja strategii ładowania

Aby upewnić się, że wybrane strategie ładowania działają optymalnie, warto mierzyć ich wpływ:

1. Narzędzia do pomiaru wydajności

  • Lighthouse - kompleksowe narzędzie do analizy wydajności
  • Chrome DevTools Performance - szczegółowa analiza ładowania zasobów
  • WebPageTest - testowanie wydajności w różnych warunkach
  • Core Web Vitals w Google Search Console

2. A/B testy strategii ładowania

1'use client';
2
3import { useState, useEffect } from 'react';
4import Image from 'next/image';
5
6export default function ABTestImage({ src, alt }) {
7  const [testGroup, setTestGroup] = useState('');
8  
9  useEffect(() => {
10    // Przypisz użytkownika do grupy A lub B
11    const group = Math.random() > 0.5 ? 'A' : 'B';
12    setTestGroup(group);
13    
14    // Zapisz dane telemetryczne
15    const trackImageLoad = (time) => {
16      // Wysyłanie danych do systemu analitycznego
17      console.log(`Image loaded in ${time}ms (Group ${group})`);
18    };
19    
20    const startTime = performance.now();
21    const onLoadComplete = () => {
22      const loadTime = performance.now() - startTime;
23      trackImageLoad(loadTime);
24    };
25    
26    // Nasłuchuj na zdarzenie load obrazu
27    const imageEl = document.getElementById('test-image');
28    if (imageEl) {
29      imageEl.addEventListener('load', onLoadComplete);
30      return () => {
31        imageEl.removeEventListener('load', onLoadComplete);
32      };
33    }
34  }, []);
35  
36  if (!testGroup) return null;
37  
38  return (
39    <div className="relative h-96 w-full">
40      <Image
41        id="test-image"
42        src={src}
43        alt={alt}
44        fill
45        style={{ objectFit: 'cover' }}
46        priority={testGroup === 'A'} // Grupa A używa priority, grupa B nie
47      />
48    </div>
49  );
50}

Dobre praktyki dotyczące strategii ładowania obrazów

  1. Używaj

    priority
    tylko dla obrazów above-the-fold - Google zaleca maksymalnie 3 obrazy z
    priority
    na stronie

  2. Domyślnie używaj lazy loading - pozwala to oszczędzić zasoby i przyspieszyć początkowe ładowanie strony

  3. Monitoruj metryki Web Vitals - regularnie sprawdzaj, jak strategie ładowania wpływają na wydajność

  4. Dostosuj strategie do rodzaju strony:

    • Strony produktowe:
      priority
      dla głównego zdjęcia produktu
    • Strony kategorii:
      lazy
      dla większości obrazów,
      eager
      dla pierwszych kilku
    • Strona główna:
      priority
      dla hero image,
      lazy
      dla pozostałych
  5. Uwzględnij urządzenia użytkowników:

    • Użytkownicy mobilni często mają wolniejsze połączenia - jeszcze ważniejsze jest użycie
      lazy
      i mniejszych obrazów
    • Użytkownicy desktopowi mogą skorzystać z
      eager
      dla lepszego doświadczenia przewijania
  6. Używaj placeholderów dla lepszego doświadczenia podczas ładowania:

    • Dla obrazów z
      priority
      - pokazuj placeholder typu
      blur
      lub
      empty
    • Dla obrazów z
      lazy
      - używaj
      blur
      dla płynnego przejścia

Scenariusze dla różnych strategii

1. Strona główna e-commerce

1export default function Homepage() {
2  return (
3    <div>
4      {/* Hero section - priority */}
5      <div className="relative h-[80vh]">
6        <Image
7          src="/images/hero.jpg"
8          alt="Witaj w Quantum Shop"
9          fill
10          style={{ objectFit: 'cover' }}
11          priority
12        />
13      </div>
14      
15      {/* Promocje - eager dla pierwszych dwóch */}
16      <div className="grid grid-cols-2 md:grid-cols-4 gap-4 my-8">
17        {promotions.map((promo, index) => (
18          <div key={promo.id} className="relative aspect-square">
19            <Image
20              src={promo.image}
21              alt={promo.title}
22              fill
23              style={{ objectFit: 'cover' }}
24              loading={index < 2 ? 'eager' : 'lazy'}
25            />
26          </div>
27        ))}
28      </div>
29      
30      {/* Kategorie produktów - lazy (domyślne) */}
31      <div className="grid grid-cols-2 md:grid-cols-3 gap-6 my-12">
32        {categories.map(category => (
33          <div key={category.id} className="relative aspect-video">
34            <Image
35              src={category.image}
36              alt={category.name}
37              fill
38              style={{ objectFit: 'cover' }}
39              // lazy loading domyślne
40            />
41          </div>
42        ))}
43      </div>
44    </div>
45  );
46}

2. Blog z długimi artykułami

1export default function BlogPost({ post }) {
2  return (
3    <article className="max-w-3xl mx-auto py-8">
4      {/* Główny obraz artykułu - priority */}
5      <div className="relative aspect-video mb-8">
6        <Image
7          src={post.featuredImage}
8          alt={post.title}
9          fill
10          style={{ objectFit: 'cover' }}
11          priority
12        />
13      </div>
14      
15      <h1 className="text-3xl font-bold mb-4">{post.title}</h1>
16      <div className="prose max-w-none">
17        {post.content.map((block, index) => {
18          if (block.type === 'paragraph') {
19            return <p key={index}>{block.content}</p>;
20          }
21          if (block.type === 'image') {
22            return (
23              <div key={index} className="relative aspect-video my-8">
24                <Image
25                  src={block.url}
26                  alt={block.caption || ''}
27                  fill
28                  style={{ objectFit: 'contain' }}
29                  // lazy loading domyślne dla obrazów w treści
30                />
31                {block.caption && (
32                  <p className="text-sm text-gray-500 mt-2 text-center">
33                    {block.caption}
34                  </p>
35                )}
36              </div>
37            );
38          }
39          return null;
40        })}
41      </div>
42      
43      {/* Powiązane artykuły - wszystkie lazy */}
44      <div className="mt-12">
45        <h2 className="text-2xl font-bold mb-6">Powiązane artykuły</h2>
46        <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
47          {post.relatedPosts.map(relatedPost => (
48            <div key={relatedPost.id} className="relative aspect-video">
49              <Image
50                src={relatedPost.thumbnail}
51                alt={relatedPost.title}
52                fill
53                style={{ objectFit: 'cover' }}
54                // Wszystkie lazy (domyślne)
55              />
56            </div>
57          ))}
58        </div>
59      </div>
60    </article>
61  );
62}

Podsumowanie

Wybór odpowiedniej strategii ładowania obrazów jest kluczowy dla optymalizacji wydajności i doświadczenia użytkownika. Podobnie jak w systemie transportowym Metropolis Quantum, gdzie pojazdy o różnym priorytecie poruszają się z różną pilnością, tak w aplikacji webowej obrazy mogą być ładowane z różnymi strategiami w zależności od ich ważności:

  1. Priority loading - dla najważniejszych obrazów "above the fold", które mają bezpośredni wpływ na LCP i pierwsze wrażenie użytkownika

  2. Eager loading - dla obrazów, które są mniej krytyczne, ale wciąż ważne, i prawdopodobnie będą potrzebne wkrótce po załadowaniu strony

  3. Lazy loading - dla większości obrazów, szczególnie tych poniżej "fold", które będą ładowane tylko wtedy, gdy użytkownik do nich dotrze

Wybierając strategię ładowania dla swoich obrazów, uwzględnij:

  • Widoczność obrazu bez przewijania
  • Wpływ na kluczowe metryki wydajności
  • Typ zawartości i jej ważność
  • Urządzenia i łącza internetowe Twoich użytkowników

Dzięki odpowiedniemu zastosowaniu strategii ładowania obrazów, możesz stworzyć aplikację, która ładuje się szybko, efektywnie zarządza zasobami i zapewnia płynne doświadczenie użytkownika - podobnie jak inteligentny system transportowy Metropolis Quantum, który zapewnia płynny przepływ pojazdów o różnych priorytetach.

W następnym rozdziale zajmiemy się optymalizacją czcionek i komponentem Font, co pozwoli nam jeszcze bardziej poprawić wydajność i estetykę naszej aplikacji Next.js 15.

Vai a CodeWorlds