Utilizziamo i cookie per migliorare la tua esperienza sul sito
CodeWorlds

Lazy loading - obrazów, tras, komponentów

Lazy loading to strategia optymalizacji polegająca na opóźnieniu ładowania zasobów do momentu, gdy stają się rzeczywiście potrzebne. Zamiast ładować wszystko od razu podczas pierwszego renderowania strony, ładujemy elementy "na żądanie" - gdy użytkownik przewija stronę, przechodzi na nową trasę, lub wykonuje akcję wymagającą określonego komponentu.

Zalety lazy loading

1. Szybszy initial load

  • Zmniejszenie rozmiaru początkowego bundle
  • Szybsze renderowanie pierwszej zawartości (FCP)
  • Lepsza percepcja wydajności przez użytkowników

2. Oszczędność zasobów

  • Mniejsze zużycie pamięci RAM
  • Redukcja transferu danych
  • Oszczędność baterii na urządzeniach mobilnych

3. Lepsze doświadczenie użytkownika

  • Płynne przewijanie bez blokowania
  • Progresywne ładowanie treści
  • Adaptacja do możliwości urządzenia i połączenia

Lazy loading obrazów

Natywny lazy loading w HTML

1<!-- Natywny lazy loading (wspierany przez nowoczesne przeglądarki) -->
2<img src="hero-image.jpg" alt="Obraz główny" loading="lazy" />
3
4<!-- Eager loading dla obrazów above-the-fold -->
5<img src="logo.jpg" alt="Logo" loading="eager" />
6
7<!-- Auto (domyślne zachowanie przeglądarki) -->
8<img src="banner.jpg" alt="Banner" loading="auto" />

Intersection Observer API

1// Implementacja lazy loading z Intersection Observer
2class LazyImageLoader {
3  constructor() {
4    this.imageObserver = new IntersectionObserver(
5      this.handleIntersection.bind(this),
6      {
7        root: null, // viewport
8        rootMargin: '50px', // ładuj 50px przed wejściem w viewport
9        threshold: 0.1 // trigger gdy 10% obrazu jest widoczne
10      }
11    );
12    
13    this.initLazyImages();
14  }
15  
16  initLazyImages() {
17    const lazyImages = document.querySelectorAll('img[data-src]');
18    lazyImages.forEach(img => {
19      this.imageObserver.observe(img);
20    });
21  }
22  
23  handleIntersection(entries) {
24    entries.forEach(entry => {
25      if (entry.isIntersecting) {
26        const img = entry.target;
27        this.loadImage(img);
28        this.imageObserver.unobserve(img);
29      }
30    });
31  }
32  
33  loadImage(img) {
34    // Ładowanie obrazu z atrybutu data-src
35    img.src = img.dataset.src;
36    img.classList.add('loaded');
37
38    // Usuwanie data-src po załadowaniu
39    delete img.dataset.src;
40
41    // Obsługa błędów ładowania
42    img.onerror = () => {
43      img.src = '/images/error-placeholder.png';
44      img.classList.add('error');
45    };
46  }
47}
48
49// Inicjalizacja
50document.addEventListener('DOMContentLoaded', () => {
51  new LazyImageLoader();
52});

React komponenty dla lazy loading obrazów

1// Hook dla lazy loading obrazów
2import { useState, useRef, useEffect } from 'react';
3
4function useIntersectionObserver(options = {}) {
5  const [isIntersecting, setIsIntersecting] = useState(false);
6  const [hasIntersected, setHasIntersected] = useState(false);
7  const elementRef = useRef(null);
8  
9  useEffect(() => {
10    const element = elementRef.current;
11    if (!element) return;
12    
13    const observer = new IntersectionObserver(
14      ([entry]) => {
15        const isElementIntersecting = entry.isIntersecting;
16        setIsIntersecting(isElementIntersecting);
17        
18        if (isElementIntersecting && !hasIntersected) {
19          setHasIntersected(true);
20        }
21      },
22      { threshold: 0.1, rootMargin: '50px', ...options }
23    );
24    
25    observer.observe(element);
26    
27    return () => observer.disconnect();
28  }, [hasIntersected, options]);
29  
30  return { elementRef, isIntersecting, hasIntersected };
31}
32
33// Komponent LazyImage
34function LazyImage({ src, alt, placeholder, className = '' }) {
35  const [imageLoaded, setImageLoaded] = useState(false);
36  const [imageError, setImageError] = useState(false);
37  const { elementRef, hasIntersected } = useIntersectionObserver();
38
39  const handleImageLoad = () => {
40    setImageLoaded(true);
41  };
42
43  const handleImageError = () => {
44    setImageError(true);
45  };
46
47  return (
48    <div ref={elementRef} className={`lazy-image-container ${className}`}>
49      {/* Placeholder wyświetlany podczas ładowania */}
50      {!imageLoaded && (
51        <img
52          src={placeholder}
53          alt={`${alt} placeholder`}
54          className="placeholder-image"
55        />
56      )}
57
58      {/* Rzeczywisty obraz ładowany lazy */}
59      {hasIntersected && (
60        <img
61          src={imageError ? '/images/error.png' : src}
62          alt={alt}
63          onLoad={handleImageLoad}
64          onError={handleImageError}
65          className={`lazy-image ${imageLoaded ? 'loaded' : 'loading'}`}
66        />
67      )}
68      
69      {/* Loading spinner */}
70      {hasIntersected && !imageLoaded && !imageError && (
71        <div className="image-spinner">Ładowanie...</div>
72      )}
73    </div>
74  );
75}
76
77// Użycie komponentu
78function Gallery({ images }) {
79  return (
80    <div className="gallery">
81      {images.map((image, index) => (
82        <LazyImage
83          key={index}
84          src={image.url}
85          alt={image.alt}
86          placeholder={image.placeholder}
87          className="gallery-item"
88        />
89      ))}
90    </div>
91  );
92}

Progressive Image Loading

1// Komponent z progresywnym ładowaniem (blur → sharp)
2function ProgressiveImage({ src, placeholder, alt }) {
3  const [imageLoaded, setImageLoaded] = useState(false);
4  const { elementRef, hasIntersected } = useIntersectionObserver();
5
6  return (
7    <div ref={elementRef} className="progressive-image">
8      {/* Rozmyty placeholder */}
9      <img
10        src={placeholder}
11        alt={alt}
12        className={`progressive-placeholder`}
13        style={{
14          filter: 'blur(10px)',
15          transition: 'opacity 0.3s ease'
16        }}
17      />
18      
19      {/* Ostry obraz */}
20      {hasIntersected && (
21        <img
22          src={src}
23          alt={alt}
24          onLoad={() => setImageLoaded(true)}
25          className={`main-image ${imageLoaded ? 'visible' : 'hidden'}`}
26          style={{
27            opacity: imageLoaded ? 1 : 0,
28            transition: 'opacity 0.3s ease'
29          }}
30        />
31      )}
32    </div>
33  );
34}

Lazy loading tras (Route-based)

React Router z Suspense

1import { Suspense, lazy } from 'react';
2import { Routes, Route } from 'react-router-dom';
3import ErrorBoundary from './components/ErrorBoundary';
4import LoadingSpinner from './components/LoadingSpinner';
5
6// Lazy loading komponentów tras
7const HomePage = lazy(() => import('./pages/HomePage'));
8const ProductsPage = lazy(() => import('./pages/ProductsPage'));
9const ProductDetailPage = lazy(() => import('./pages/ProductDetailPage'));
10const UserProfilePage = lazy(() => import('./pages/UserProfilePage'));
11const AdminPanel = lazy(() => import('./pages/AdminPanel'));
12
13// Komponenty z custom loading i error handling
14const LazyRoute = ({ children }) => (
15  <ErrorBoundary>
16    <Suspense fallback={<LoadingSpinner />}>
17      {children}
18    </Suspense>
19  </ErrorBoundary>
20);
21
22function App() {
23  return (
24    <Routes>
25      <Route 
26        path="/" 
27        element={
28          <LazyRoute>
29            <HomePage />
30          </LazyRoute>
31        } 
32      />
33      <Route 
34        path="/products" 
35        element={
36          <LazyRoute>
37            <ProductsPage />
38          </LazyRoute>
39        } 
40      />
41      <Route 
42        path="/products/:id" 
43        element={
44          <LazyRoute>
45            <ProductDetailPage />
46          </LazyRoute>
47        } 
48      />
49      <Route 
50        path="/profile" 
51        element={
52          <LazyRoute>
53            <UserProfilePage />
54          </LazyRoute>
55        } 
56      />
57      <Route 
58        path="/admin/*" 
59        element={
60          <LazyRoute>
61            <AdminPanel />
62          </LazyRoute>
63        } 
64      />
65    </Routes>
66  );
67}

Zaawansowane strategie ładowania tras

1// Preloading tras na podstawie user behavior
2class RoutePreloader {
3  constructor() {
4    this.preloadedRoutes = new Set();
5    this.setupPreloading();
6  }
7  
8  setupPreloading() {
9    // Preload na hover
10    this.setupHoverPreloading();
11    
12    // Preload na podstawie prawdopodobieństwa
13    this.setupPredictivePreloading();
14    
15    // Preload w idle time
16    this.setupIdlePreloading();
17  }
18  
19  setupHoverPreloading() {
20    document.addEventListener('mouseover', (e) => {
21      const link = e.target.closest('a[href]');
22      if (link && this.shouldPreload(link.href)) {
23        this.preloadRoute(link.href);
24      }
25    });
26  }
27  
28  setupPredictivePreloading() {
29    // Jeśli użytkownik jest na /products, preload /products/:id
30    const currentPath = window.location.pathname;
31    
32    if (currentPath === '/products') {
33      // Delay żeby nie wpływać na current page load
34      setTimeout(() => {
35        this.preloadRoute('/products/popular');
36      }, 2000);
37    }
38  }
39  
40  setupIdlePreloading() {
41    if ('requestIdleCallback' in window) {
42      requestIdleCallback(() => {
43        this.preloadCriticalRoutes();
44      });
45    }
46  }
47  
48  preloadRoute(path) {
49    if (this.preloadedRoutes.has(path)) return;
50    
51    const routeMap = {
52      '/products': () => import('./pages/ProductsPage'),
53      '/profile': () => import('./pages/UserProfilePage'),
54      '/admin': () => import('./pages/AdminPanel')
55    };
56    
57    const preloadFunction = routeMap[path];
58    if (preloadFunction) {
59      preloadFunction().then(() => {
60        this.preloadedRoutes.add(path);
61        console.log(`Route ${path} preloaded`);
62      });
63    }
64  }
65  
66  shouldPreload(href) {
67    // Preload tylko internal links
68    return href.startsWith(window.location.origin);
69  }
70  
71  preloadCriticalRoutes() {
72    const critical = ['/products', '/profile'];
73    critical.forEach(route => this.preloadRoute(route));
74  }
75}
76
77// Inicjalizacja
78new RoutePreloader();

Lazy loading komponentów

Component-level lazy loading

1// Lazy loading drogich komponentów
2const ExpensiveChart = lazy(() => import('./components/ExpensiveChart'));
3const DataVisualization = lazy(() => import('./components/DataVisualization'));
4const VideoPlayer = lazy(() => import('./components/VideoPlayer'));
5const CodeEditor = lazy(() => import('./components/CodeEditor'));
6
7function Dashboard({ activeTab, data }) {
8  return (
9    <div className="dashboard">
10      <nav className="dashboard-nav">
11        {/* Nawigacja zawsze widoczna */}
12      </nav>
13      
14      <main className="dashboard-content">
15        {activeTab === 'charts' && (
16          <Suspense fallback={<div>Ładowanie wykresów...</div>}>
17            <ExpensiveChart data={data} />
18          </Suspense>
19        )}
20        
21        {activeTab === 'visualization' && (
22          <Suspense fallback={<div>Ładowanie wizualizacji...</div>}>
23            <DataVisualization data={data} />
24          </Suspense>
25        )}
26        
27        {activeTab === 'video' && (
28          <Suspense fallback={<div>Ładowanie odtwarzacza...</div>}>
29            <VideoPlayer />
30          </Suspense>
31        )}
32        
33        {activeTab === 'editor' && (
34          <Suspense fallback={<div>Ładowanie edytora...</div>}>
35            <CodeEditor />
36          </Suspense>
37        )}
38      </main>
39    </div>
40  );
41}

Modal i drawer lazy loading

1// Hook dla lazy modali
2function useLazyModal() {
3  const [isOpen, setIsOpen] = useState(false);
4  const [Component, setComponent] = useState(null);
5  
6  const openModal = async (modalType) => {
7    const modalMap = {
8      'user-settings': () => import('./modals/UserSettingsModal'),
9      'payment': () => import('./modals/PaymentModal'),
10      'image-editor': () => import('./modals/ImageEditorModal'),
11    };
12    
13    const importFunction = modalMap[modalType];
14    if (importFunction) {
15      const { default: ModalComponent } = await importFunction();
16      setComponent(() => ModalComponent);
17      setIsOpen(true);
18    }
19  };
20  
21  const closeModal = () => {
22    setIsOpen(false);
23    // Opcjonalnie: unload komponentu po zamknięciu
24    setTimeout(() => setComponent(null), 300);
25  };
26  
27  return { Component, isOpen, openModal, closeModal };
28}
29
30// Użycie lazy modali
31function App() {
32  const { Component: ModalComponent, isOpen, openModal, closeModal } = useLazyModal();
33  
34  return (
35    <div>
36      <button onClick={() => openModal('user-settings')}>
37        Ustawienia użytkownika
38      </button>
39      <button onClick={() => openModal('payment')}>
40        Płatność
41      </button>
42      <button onClick={() => openModal('image-editor')}>
43        Edytor obrazów
44      </button>
45      
46      {isOpen && ModalComponent && (
47        <Suspense fallback={<div>Ładowanie...</div>}>
48          <ModalComponent onClose={closeModal} />
49        </Suspense>
50      )}
51    </div>
52  );
53}

Feature-based lazy loading

1// Lazy loading całych feature'ów
2const AdminFeature = lazy(() => import('./features/Admin'));
3const AnalyticsFeature = lazy(() => import('./features/Analytics'));
4const ShoppingCartFeature = lazy(() => import('./features/ShoppingCart'));
5
6function FeatureLoader({ feature, ...props }) {
7  const featureMap = {
8    admin: AdminFeature,
9    analytics: AnalyticsFeature,
10    cart: ShoppingCartFeature
11  };
12  
13  const FeatureComponent = featureMap[feature];
14  
15  if (!FeatureComponent) {
16    return <div>Nieznana funkcjonalność: {feature}</div>;
17  }
18  
19  return (
20    <ErrorBoundary>
21      <Suspense fallback={<FeatureLoadingSkeleton feature={feature} />}>
22        <FeatureComponent {...props} />
23      </Suspense>
24    </ErrorBoundary>
25  );
26}
27
28// Skeleton dla różnych feature'ów
29function FeatureLoadingSkeleton({ feature }) {
30  const skeletons = {
31    admin: <AdminSkeleton />,
32    analytics: <ChartsSkeleton />,
33    cart: <CartSkeleton />
34  };
35  
36  return skeletons[feature] || <DefaultSkeleton />;
37}

Zaawansowane techniki lazy loading

Virtual scrolling z lazy loading

1// Virtual scrolling dla dużych list
2import { FixedSizeList as List } from 'react-window';
3
4function VirtualizedList({ items, itemHeight = 50 }) {
5  const [loadedItems, setLoadedItems] = useState(new Set());
6  
7  const loadItem = useCallback(async (index) => {
8    if (loadedItems.has(index)) return;
9    
10    // Symulacja ładowania danych
11    await new Promise(resolve => setTimeout(resolve, 100));
12    setLoadedItems(prev => new Set([...prev, index]));
13  }, [loadedItems]);
14  
15  const Row = ({ index, style }) => {
16    const item = items[index];
17    const isLoaded = loadedItems.has(index);
18    
19    useEffect(() => {
20      if (!isLoaded) {
21        loadItem(index);
22      }
23    }, [index, isLoaded, loadItem]);
24    
25    return (
26      <div style={style} className="list-item">
27        {isLoaded ? (
28          <ItemContent item={item} />
29        ) : (
30          <ItemSkeleton />
31        )}
32      </div>
33    );
34  };
35  
36  return (
37    <List
38      height={400}
39      itemCount={items.length}
40      itemSize={itemHeight}
41      overscanCount={5} // Preload 5 items poza viewport
42    >
43      {Row}
44    </List>
45  );
46}

Lazy loading z cache

1// Cache dla lazy-loaded komponentów
2class ComponentCache {
3  constructor() {
4    this.cache = new Map();
5  }
6  
7  async get(key, loader) {
8    if (this.cache.has(key)) {
9      return this.cache.get(key);
10    }
11    
12    const component = await loader();
13    this.cache.set(key, component);
14    return component;
15  }
16  
17  preload(key, loader) {
18    if (!this.cache.has(key)) {
19      this.get(key, loader);
20    }
21  }
22  
23  clear(key) {
24    if (key) {
25      this.cache.delete(key);
26    } else {
27      this.cache.clear();
28    }
29  }
30}
31
32const componentCache = new ComponentCache();
33
34// Hook wykorzystujący cache
35function useLazyComponent(componentKey, loader) {
36  const [Component, setComponent] = useState(null);
37  const [loading, setLoading] = useState(false);
38  const [error, setError] = useState(null);
39  
40  const loadComponent = useCallback(async () => {
41    if (Component) return;
42    
43    setLoading(true);
44    setError(null);
45    
46    try {
47      const loadedComponent = await componentCache.get(componentKey, loader);
48      setComponent(() => loadedComponent.default);
49    } catch (err) {
50      setError(err);
51    } finally {
52      setLoading(false);
53    }
54  }, [componentKey, loader, Component]);
55  
56  // Preload funkcja
57  const preload = useCallback(() => {
58    componentCache.preload(componentKey, loader);
59  }, [componentKey, loader]);
60  
61  return { Component, loading, error, loadComponent, preload };
62}

Connection-aware lazy loading

1// Adaptacyjne ładowanie na podstawie połączenia
2function useConnectionAwareLazyLoading() {
3  const [connectionType, setConnectionType] = useState('4g');
4  
5  useEffect(() => {
6    const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
7    
8    if (connection) {
9      setConnectionType(connection.effectiveType);
10      
11      const updateConnection = () => {
12        setConnectionType(connection.effectiveType);
13      };
14      
15      connection.addEventListener('change', updateConnection);
16      return () => connection.removeEventListener('change', updateConnection);
17    }
18  }, []);
19  
20  const shouldLazyLoad = useCallback((priority = 'normal') => {
21    const strategies = {
22      '4g': { immediate: true, normal: true, low: true },
23      '3g': { immediate: true, normal: true, low: false },
24      '2g': { immediate: true, normal: false, low: false },
25      'slow-2g': { immediate: false, normal: false, low: false }
26    };
27    
28    return strategies[connectionType]?.[priority] ?? false;
29  }, [connectionType]);
30  
31  const getLoadingStrategy = useCallback(() => {
32    const strategies = {
33      '4g': 'aggressive', // Ładuj wszystko
34      '3g': 'moderate',   // Ładuj tylko gdy potrzebne
35      '2g': 'conservative', // Ładuj minimalnie
36      'slow-2g': 'minimal' // Tylko niezbędne
37    };
38    
39    return strategies[connectionType] || 'moderate';
40  }, [connectionType]);
41  
42  return { connectionType, shouldLazyLoad, getLoadingStrategy };
43}
44
45// Komponent adaptujący się do połączenia
46function AdaptiveImageGrid({ images }) {
47  const { shouldLazyLoad, getLoadingStrategy } = useConnectionAwareLazyLoading();
48  const strategy = getLoadingStrategy();
49  
50  const imageSettings = {
51    aggressive: { quality: 'high', eager: 10 },
52    moderate: { quality: 'medium', eager: 4 },
53    conservative: { quality: 'low', eager: 2 },
54    minimal: { quality: 'thumbnail', eager: 1 }
55  };
56
57  const settings = imageSettings[strategy];
58
59  return (
60    <div className="image-grid">
61      {images.map((image, index) => (
62        <AdaptiveImage
63          key={image.id}
64          src={image.url}
65          quality={settings.quality}
66          placeholder={image.placeholder}
67          loading={index < settings.eager ? 'eager' : 'lazy'}
68          shouldOptimize={strategy !== 'aggressive'}
69        />
70      ))}
71    </div>
72  );
73}

Performance monitoring dla lazy loading

1// Monitoring wydajności lazy loading
2class LazyLoadingMonitor {
3  constructor() {
4    this.metrics = {
5      componentsLoaded: 0,
6      totalLoadTime: 0,
7      failedLoads: 0,
8      cacheHits: 0
9    };
10    
11    this.startTime = performance.now();
12  }
13  
14  trackComponentLoad(componentName, loadTime, fromCache = false) {
15    this.metrics.componentsLoaded++;
16    this.metrics.totalLoadTime += loadTime;
17    
18    if (fromCache) {
19      this.metrics.cacheHits++;
20    }
21    
22    // Web Vitals tracking
23    if (loadTime > 2500) { // Slow loading threshold
24      console.warn(`Slow lazy load detected: ${componentName} took ${loadTime}ms`);
25    }
26    
27    // Send to analytics
28    this.sendMetrics('component_lazy_load', {
29      component: componentName,
30      loadTime,
31      fromCache,
32      timestamp: Date.now()
33    });
34  }
35  
36  trackFailedLoad(componentName, error) {
37    this.metrics.failedLoads++;
38    
39    console.error(`Failed to lazy load ${componentName}:`, error);
40    
41    this.sendMetrics('component_lazy_load_error', {
42      component: componentName,
43      error: error.message,
44      timestamp: Date.now()
45    });
46  }
47  
48  getAverageLoadTime() {
49    return this.metrics.componentsLoaded > 0 
50      ? this.metrics.totalLoadTime / this.metrics.componentsLoaded 
51      : 0;
52  }
53  
54  getCacheHitRate() {
55    return this.metrics.componentsLoaded > 0
56      ? (this.metrics.cacheHits / this.metrics.componentsLoaded) * 100
57      : 0;
58  }
59  
60  sendMetrics(event, data) {
61    // Integracja z Google Analytics, Mixpanel, itp.
62    if (typeof gtag !== 'undefined') {
63      gtag('event', event, data);
64    }
65  }
66  
67  generateReport() {
68    const sessionTime = performance.now() - this.startTime;
69    
70    return {
71      sessionDuration: sessionTime,
72      componentsLoaded: this.metrics.componentsLoaded,
73      averageLoadTime: this.getAverageLoadTime(),
74      cacheHitRate: this.getCacheHitRate(),
75      failureRate: (this.metrics.failedLoads / this.metrics.componentsLoaded) * 100,
76      totalFailures: this.metrics.failedLoads
77    };
78  }
79}
80
81// Singleton instance
82const lazyLoadMonitor = new LazyLoadingMonitor();
83
84// Hook z monitoringiem
85function useMonitoredLazyComponent(componentName, loader) {
86  const [Component, setComponent] = useState(null);
87  const [loading, setLoading] = useState(false);
88  
89  const loadComponent = useCallback(async () => {
90    if (Component) return;
91    
92    const startTime = performance.now();
93    setLoading(true);
94    
95    try {
96      const loadedComponent = await loader();
97      const loadTime = performance.now() - startTime;
98      
99      setComponent(() => loadedComponent.default);
100      lazyLoadMonitor.trackComponentLoad(componentName, loadTime);
101    } catch (error) {
102      lazyLoadMonitor.trackFailedLoad(componentName, error);
103      throw error;
104    } finally {
105      setLoading(false);
106    }
107  }, [componentName, loader, Component]);
108  
109  return { Component, loading, loadComponent };
110}

Best practices dla lazy loading

1. Priorytyzacja treści

1// Above-the-fold content - eager loading
2// Below-the-fold content - lazy loading
3function ContentStrategy() {
4  return (
5    <>
6      {/* Critical path - eager */}
7      <Header />
8      <Hero />
9      <MainContent />
10      
11      {/* Secondary content - lazy */}
12      <Suspense fallback={<SectionSkeleton />}>
13        <LazySection name="testimonials" />
14      </Suspense>
15      
16      <Suspense fallback={<SectionSkeleton />}>
17        <LazySection name="newsletter" />
18      </Suspense>
19      
20      {/* Low priority - very lazy */}
21      <Suspense fallback={<FooterSkeleton />}>
22        <LazyFooter />
23      </Suspense>
24    </>
25  );
26}

2. Graceful fallbacks

1// Zawsze zapewnij graceful fallbacks
2function LazyComponentWrapper({ children, fallback, errorFallback }) {
3  return (
4    <ErrorBoundary 
5      fallback={errorFallback || <div>Wystąpił błąd podczas ładowania</div>}
6    >
7      <Suspense fallback={fallback || <LoadingSkeleton />}>
8        {children}
9      </Suspense>
10    </ErrorBoundary>
11  );
12}

3. Preloading strategiczny

1// Strategiczne preloadowanie na podstawie user behavior
2function useStrategicPreloading() {
3  useEffect(() => {
4    // Preload po 2 sekundach bezaktywności
5    const timeoutId = setTimeout(() => {
6      // Preload prawdopodobnych następnych komponentów
7      import('./components/UserProfile');
8      import('./components/ShoppingCart');
9    }, 2000);
10    
11    // Preload na podstawie mouse movement w kierunku linka
12    const handleMouseMove = (e) => {
13      // Jeśli kursor zbliża się do określonego elementu
14      // preload związany komponent
15    };
16    
17    document.addEventListener('mousemove', handleMouseMove);
18    
19    return () => {
20      clearTimeout(timeoutId);
21      document.removeEventListener('mousemove', handleMouseMove);
22    };
23  }, []);
24}

Podsumowanie

Lazy loading to potężna technika optymalizacji, która:

  1. Redukuje initial bundle size - szybsze uruchamianie aplikacji
  2. Poprawia Core Web Vitals - lepsze FCP, LCP, CLS
  3. Oszczędza zasoby - pamięć, transfer, bateria
  4. Skaluje z aplikacją - automatyczna optymalizacja przy dodawaniu feature'ów
  5. Adaptuje się do warunków - responsive loading na podstawie połączenia

Kluczem do sukcesu jest zbalansowanie między wydajnością a doświadczeniem użytkownika - lazy loading powinien być niewidoczny dla użytkownika, a jednocześnie znacząco poprawiać wydajność aplikacji.

Vai a CodeWorlds