Dynamiczne importowanie komponentów w Next.js

Wydajność aplikacji webowych jest jednym z najważniejszych aspektów, które wpływają na doświadczenie użytkownika. Jednym z najskuteczniejszych sposobów poprawy wydajności jest dynamiczne importowanie komponentów — technika, która pozwala na załadowanie kodu tylko wtedy, gdy jest on rzeczywiście potrzebny. W Next.js to zadanie staje się bardzo proste, dzięki wbudowanej funkcji next/dynamic. W tym artykule przyjrzymy się, czym jest dynamiczne importowanie w Next.js, jak poprawić wydajność aplikacji za pomocą tej funkcji oraz jak wykorzystać lazy loading komponentów.

1. Co to jest dynamiczne importowanie w Next.js?

Dynamiczne importowanie w Next.js pozwala na asynchroniczne ładowanie komponentów lub modułów w momencie, gdy są one rzeczywiście potrzebne. Dzięki temu możemy podzielić naszą aplikację na mniejsze części (tzw. chunki), które będą ładowane dopiero wtedy, gdy użytkownik wejdzie na stronę, która ich potrzebuje. To podejście pozwala znacząco zmniejszyć początkowy rozmiar aplikacji, co przekłada się na szybsze ładowanie strony.

Jak działa dynamiczne importowanie?

W tradycyjnym przypadku, kiedy importujemy komponenty w React (np. import MyComponent from './MyComponent'), cały kod komponentu jest ładowany wraz z początkowym pakietem JavaScript. Przy dużych aplikacjach, może to prowadzić do długiego czasu ładowania strony. Dynamiczne importowanie pozwala na opóźnienie ładowania komponentu, a zamiast tego ładować go dopiero wtedy, gdy użytkownik interaguje z danym elementem lub przechodzi do strony, która tego komponentu wymaga.

W Next.js możesz to zrobić za pomocą funkcji next/dynamic.

2. Jak poprawić wydajność aplikacji za pomocą next/dynamic?

next/dynamic to funkcja, która umożliwia dynamiczne ładowanie komponentów. Możesz jej użyć do importowania komponentów asynchronicznie, co oznacza, że nie będą one ładowane przy pierwszym renderze, lecz dopiero wtedy, gdy będą faktycznie potrzebne.

Instalacja i konfiguracja

Nie musisz instalować żadnych dodatkowych pakietów, aby zacząć używać dynamicznego importowania w Next.js. Wszystko, czego potrzebujesz, znajduje się w samym frameworku.

Jak zaimplementować dynamiczne importowanie?

Aby użyć next/dynamic, wystarczy zaimportować ją w pliku, w którym chcesz załadować komponent dynamicznie, a następnie opakować komponent, który ma być załadowany.

Przykład:

Załóżmy, że mamy komponent, który jest dość ciężki, np. wykres, który jest renderowany tylko w przypadku, gdy użytkownik przewinie stronę do określonego miejsca. Możemy zaimplementować dynamiczne importowanie, aby załadować ten komponent tylko wtedy, gdy jest on faktycznie widoczny.

  1. Stwórzmy komponent HeavyChart.js, który będzie renderował ciężki wykres (na przykład z biblioteką Chart.js):
				
					// components/HeavyChart.js
import { Line } from 'react-chartjs-2';

const HeavyChart = () => {
  const data = {
    labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May'],
    datasets: [
      {
        label: 'Przykładowe dane',
        data: [65, 59, 80, 81, 56],
      },
    ],
  };

  return <Line data={data} />;
};

export default HeavyChart;

				
			
  1. Teraz możemy zaimportować ten komponent dynamicznie w pliku, np. w pages/index.js, używając funkcji dynamic:
				
					// pages/index.js
import dynamic from 'next/dynamic';

// Dynamiczne importowanie komponentu
const DynamicHeavyChart = dynamic(() => import('../components/HeavyChart'), {
  loading: () => <p>Ładowanie wykresu...</p>,  // Komponent ładowania
});

const Home = () => {
  return (
    <div>
      <h1>Witamy w aplikacji Next.js</h1>
      <p>Nasza aplikacja jest bardziej wydajna dzięki dynamicznemu importowaniu!</p>

      {/* Komponent zostanie załadowany tylko wtedy, gdy jest potrzebny */}
      <DynamicHeavyChart />
    </div>
  );
};

export default Home;

				
			

Jak działa powyższy kod?

  • Dynamiczne importowanie: Funkcja dynamic ładuje komponent HeavyChart dopiero, gdy jest on rzeczywiście renderowany na stronie. Przed załadowaniem komponentu wyświetlana jest wiadomość „Ładowanie wykresu…”.
  • Ładowanie na żądanie: Komponent HeavyChart jest ładowany asynchronicznie, co oznacza, że nie obciąża on początkowego czasu ładowania strony, a jest ładowany tylko wtedy, gdy użytkownik przewinie stronę lub gdy komponent jest rzeczywiście potrzebny.
  • Zaleta: Redukcja rozmiaru początkowego pakietu JavaScript, co przyspiesza czas ładowania strony i poprawia wydajność aplikacji.

3. Lazy loading komponentów i kodu

Lazy loading to technika, która pozwala na ładowanie komponentów tylko wtedy, gdy są one potrzebne. Dzięki lazy loadingu możemy podzielić naszą aplikację na mniejsze części i załadować je na żądanie, zamiast ładować wszystko na raz przy pierwszym renderze.

W Next.js, dynamiczne importowanie działa na zasadzie lazy loadingu, co oznacza, że komponenty będą ładowane tylko wtedy, gdy użytkownik rzeczywiście je zobaczy (lub gdy będą one wymagane). Dzięki temu możemy zaoszczędzić na czasie ładowania początkowego i poprawić wrażenia użytkownika.

Przykład lazy loadingu komponentu:

Załóżmy, że mamy stronę, na której są dwa komponenty: jeden lekki i szybki w ładowaniu, oraz drugi, który jest znacznie większy i zawiera zaawansowaną logikę, jak interaktywne wykresy. Chcemy, aby wykres był ładowany dopiero wtedy, gdy użytkownik kliknie przycisk, który go wywołuje.

  1. Stwórzmy komponent LargeComponent.js, który będzie zawierał cięższy, interaktywny wykres:
				
					// components/LargeComponent.js
import { Line } from 'react-chartjs-2';

const LargeComponent = () => {
  const data = {
    labels: ['January', 'February', 'March', 'April'],
    datasets: [
      {
        label: 'Wzrost sprzedaży',
        data: [10, 30, 20, 40],
      },
    ],
  };

  return <Line data={data} />;
};

export default LargeComponent;

				
			
  1. Zaimportujmy ten komponent dynamicznie, ale tylko wtedy, gdy użytkownik kliknie przycisk:
				
					// pages/index.js
import dynamic from 'next/dynamic';
import { useState } from 'react';

// Dynamiczne importowanie komponentu
const DynamicLargeComponent = dynamic(() => import('../components/LargeComponent'), {
  loading: () => <p>Ładowanie wykresu...</p>,
});

const Home = () => {
  const [showChart, setShowChart] = useState(false);

  return (
    <div>
      <h1>Lazy Loading w Next.js</h1>
      <p>Wykres jest ładowany tylko wtedy, gdy go potrzebujemy!</p>

      <button onClick={() => setShowChart(true)}>Pokaż wykres</button>

      {showChart && <DynamicLargeComponent />}
    </div>
  );
};

export default Home;

				
			

Jak działa lazy loading w tym przykładzie?

  • Komponent LargeComponent (z wykresem) nie jest ładowany przy pierwszym renderze strony. Zamiast tego, ładowanie następuje dopiero po kliknięciu przycisku „Pokaż wykres”.
  • Dzięki dynamic i lazy loadingowi, komponent LargeComponent jest ładowany asynchronicznie w momencie, kiedy użytkownik go naprawdę potrzebuje, co zmniejsza początkowy czas ładowania strony.
  • Zaleta: Zmniejszenie początkowego czasu ładowania strony, co poprawia UX, szczególnie w aplikacjach o dużych komponentach lub skomplikowanej logice.

Przykład 1: Ładowanie dużych komponentów tylko na żądanie

Załóżmy, że mamy aplikację, w której część komponentów jest bardzo „ciężka” – na przykład komponenty renderujące wykresy, tabele lub mapy. Chcielibyśmy, aby takie komponenty były ładowane dopiero wtedy, gdy użytkownik przejdzie do odpowiedniej sekcji strony lub kliknie w odpowiedni przycisk.

Scenariusz:

Mamy stronę z wykresem, który jest dużym komponentem wymagającym sporych zasobów. Nie chcemy go ładować od razu, ponieważ to opóźniłoby czas ładowania strony. Zamiast tego, chcemy załadować ten wykres tylko wtedy, gdy użytkownik zdecyduje się go zobaczyć.

Implementacja:

  1. Komponent Chart.js (Wykres):
				
					// components/HeavyChart.js
import { Line } from 'react-chartjs-2';

const HeavyChart = () => {
  const data = {
    labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May'],
    datasets: [
      {
        label: 'Przykładowe dane',
        data: [65, 59, 80, 81, 56],
      },
    ],
  };

  return <Line data={data} />;
};

export default HeavyChart;

				
			
  1. Strona z dynamicznym importem:
				
					// pages/index.js
import dynamic from 'next/dynamic';
import { useState } from 'react';

// Dynamiczne importowanie komponentu
const DynamicHeavyChart = dynamic(() => import('../components/HeavyChart'), {
  loading: () => <p>Ładowanie wykresu...</p>,  // Komponent ładowania
});

const Home = () => {
  const [showChart, setShowChart] = useState(false);

  return (
    <div>
      <h1>Witamy w aplikacji Next.js</h1>
      <p>Nasza aplikacja jest bardziej wydajna dzięki dynamicznemu importowaniu!</p>

      <button onClick={() => setShowChart(true)}>Pokaż wykres</button>

      {/* Komponent wykresu jest ładowany tylko po kliknięciu przycisku */}
      {showChart && <DynamicHeavyChart />}
    </div>
  );
};

export default Home;

				
			

Jak działa ten kod?

  • Dynamiczny import: Komponent HeavyChart zostaje załadowany dopiero po kliknięciu przycisku „Pokaż wykres”. Dzięki temu nie blokujemy ładowania strony.
  • Lazy loading: Wykres ładowany jest tylko wtedy, gdy użytkownik zdecyduje się go zobaczyć, co pozwala na zaoszczędzenie zasobów przy pierwszym ładowaniu strony.
  • Komponent ładowania: Zamiast pokazania pustej strony, wyświetlamy komunikat „Ładowanie wykresu…” do momentu, aż komponent zostanie załadowany.

Przykład 2: Ładowanie komponentu po przewinięciu strony (scroll)

W tym przypadku, komponenty nie muszą być ładowane od razu. Zamiast tego, możemy załadować je tylko wtedy, gdy użytkownik przewinie stronę do sekcji, która wymaga tego komponentu.

Scenariusz:

Mamy stronę z kilkoma sekcjami i chcemy, aby komponenty były ładowane dopiero w momencie, kiedy użytkownik przewinie do odpowiedniej sekcji. Jest to dobry sposób na poprawienie wydajności w aplikacjach z wieloma dynamicznymi treściami.

Implementacja:

  1. Komponent LazyComponent.js:
				
					// components/LazyComponent.js
const LazyComponent = () => {
  return <div style={{ height: '400px', backgroundColor: '#ccc' }}>Dynamiczny komponent</div>;
};

export default LazyComponent;

				
			
  1. Strona z dynamicznym importem na podstawie scrolla:
				
					// pages/index.js
import dynamic from 'next/dynamic';
import { useState, useEffect } from 'react';

// Dynamiczne importowanie komponentu
const DynamicLazyComponent = dynamic(() => import('../components/LazyComponent'), {
  loading: () => <p>Ładowanie komponentu...</p>,  // Komponent ładowania
});

const Home = () => {
  const [showComponent, setShowComponent] = useState(false);

  const handleScroll = () => {
    if (window.scrollY > 300) {
      setShowComponent(true);  // Pokaż komponent, gdy użytkownik przewinie stronę
    }
  };

  useEffect(() => {
    window.addEventListener('scroll', handleScroll);
    return () => window.removeEventListener('scroll', handleScroll); // Usuwamy nasłuchiwanie po zakończeniu
  }, []);

  return (
    <div style={{ height: '1500px' }}>
      <h1>Dynamiczne ładowanie przy przewijaniu</h1>
      <p>Przewiń stronę, aby załadować komponent!</p>

      {/* Komponent ładowany tylko, gdy użytkownik przewinie stronę do określonego punktu */}
      {showComponent && <DynamicLazyComponent />}
    </div>
  );
};

export default Home;

				
			

Jak działa ten kod?

  • Zdarzenie scroll: Używamy zdarzenia scroll, aby sprawdzić, czy użytkownik przewinął stronę do odpowiedniej sekcji.
  • Dynamiczny import: Komponent LazyComponent zostaje załadowany dopiero po przewinięciu strony do odpowiedniej sekcji, co pozwala zaoszczędzić zasoby przy pierwszym ładowaniu strony.
  • Lazy loading: Dzięki dynamicznemu importowaniu i lazy loadingowi komponentu, aplikacja jest bardziej wydajna, bo ładuje komponenty tylko wtedy, gdy są one faktycznie potrzebne.

Przykład 3: Dynamiczne ładowanie zewnętrznych bibliotek

Czasami możesz mieć do czynienia z zewnętrznymi bibliotekami, które są bardzo ciężkie lub których funkcje nie są zawsze potrzebne. W takich przypadkach możesz załadować je tylko wtedy, gdy są wymagane.

Scenariusz:

Mamy aplikację, w której korzystamy z dużej biblioteki, jak np. Chart.js do generowania wykresów. Zamiast ładować całą bibliotekę na początku, chcemy załadować ją dopiero wtedy, gdy użytkownik zażąda wyświetlenia wykresu.

Implementacja:

  1. Dynamiczne ładowanie wykresu z użyciem Chart.js:
				
					// components/ChartComponent.js
import { useEffect } from 'react';

const ChartComponent = () => {
  useEffect(() => {
    import('chart.js').then(({ Chart }) => {
      const ctx = document.getElementById('myChart').getContext('2d');
      new Chart(ctx, {
        type: 'line',
        data: {
          labels: ['January', 'February', 'March', 'April'],
          datasets: [{
            label: 'Sales',
            data: [30, 20, 50, 70],
          }]
        }
      });
    });
  }, []);

  return <canvas id="myChart" width="400" height="200"></canvas>;
};

export default ChartComponent;

				
			
  1. Strona z dynamicznym ładowaniem biblioteki:
				
					// pages/index.js
import dynamic from 'next/dynamic';

const DynamicChartComponent = dynamic(() => import('../components/ChartComponent'), {
  loading: () => <p>Ładowanie wykresu...</p>,
});

const Home = () => {
  return (
    <div>
      <h1>Dynamiczne ładowanie wykresu</h1>
      <DynamicChartComponent />
    </div>
  );
};

export default Home;

				
			

Jak działa ten kod?

  • Zewnętrzna biblioteka: Biblioteka chart.js jest załadowana tylko wtedy, gdy komponent wykresu (ChartComponent) zostaje renderowany na stronie.
  • Dynamiczne importowanie: Dzięki dynamicznemu importowi, całe ciężkie zależności są ładowane tylko wtedy, gdy są potrzebne, co poprawia wydajność aplikacji.

Podsumowanie

Dynamiczne importowanie komponentów w Next.js, realizowane za pomocą next/dynamic, jest potężnym narzędziem, które pomaga w optymalizacji wydajności aplikacji. Dzięki temu możemy załadować komponenty asynchronicznie, zmniejszając początkowy rozmiar aplikacji i skracając czas ładowania strony. Możliwość zastosowania lazy loadingu komponentów, w połączeniu z dynamicznym importowaniem, pozwala na budowanie wydajnych aplikacji, które są szybsze i bardziej responsywne.