Merhaba arkadaşlar, bugün başımı çok ağrıtan bir sorunu ve onun en temiz çözümünü anlatacağım. Projemde, ana sayfada kullanıcıyı selamlayan devasa bir "hero" bölümü, ardından yüzlerce ürün kartı ve en sonunda da ağır yorum bileşenleri vardı. Sayfa ilk açıldığında, kullanıcı henüz görmediği ve belki de hiç kaydırıp görmeyeceği tüm bu component'lar DOM'a yükleniyor, bundle boyutu şişiyor ve Lighthouse performans skoru yerlerde sürünüyordu. İşte tam burada Intersection Observer API imdadıma yetişti.
Sorunun Tam Olarak Nerede Olduğunu Anlamak
Sorun şuydu: Kullanıcı sadece ilk ekranda gördüğü içerikle etkileşime geçmek istiyordu ama tarayıcı, sayfanın en altındaki yorum bileşeninin CSS'sini, JS'ini ve resimlerini de indirip işliyordu. Bu, özellikle mobilde ve düşük performanslı cihazlarda ciddi bir gecikmeye neden oluyordu. "Acaba tüm bu içeriği lazy loading ile mi ayırsam?" diye düşünürken, daha native ve modern bir çözüm olan Intersection Observer'ı keşfettim.
Intersection Observer Nedir ve Nasıl Çalışır?
Kısaca, bu API bize bir DOM elementinin viewport (görünür alan) ile kesişimini izleme imkanı veriyor. Yani bir div, viewport'a girdiği veya çıktığı anda bizi haberdar edebiliyor. Bu özelliği kullanarak, bir component'ı sadece görünür olacağı zaman DOM'a inject edebilir veya render ettirebiliriz.
İşte React projemde kullandığım temel bir hook yapısı:
Hook'u Component'larda Nasıl Kullandım?
Artık elimde sihirli bir hook var. Ağır olan ve ilk görünümde gerekli olmayan her component'ı bu hook ile sarmalayabilirim. İşte bir ürün listesi component'ı örneği:
Bu yapıda, HeavyProductCard component'ı ve onun içerdiği tüm modüller (resimler, ikon kütüphaneleri vs.), kullanıcı o bölüme yaklaşana kadar yüklenmiyor. rootMargin ile biraz daha erken tetikleyip, yükleme sırasında oluşabilecek boşluk hissini de azaltabilirsiniz.
Elde Ettiğim Sonuçlar ve Çıkarımlar
Bu tekniği sayfanın alt bölümlerindeki yorumlar, önerilen ürünler ve sosyal medya feed'leri gibi alanlara uyguladıktan sonra, ilk sayfa yükünde gözle görülür bir iyileşme oldu. First Contentful Paint (FCP) ve Largest Contentful Paint (LCP) metriklerim ciddi oranda düzeldi. En güzeli de, kullanıcı deneyiminden hiç ödün vermemiş olmam. Onlar kaydırdıkça içerik sihirli bir şekilde beliriyor.
Siz de projelerinizde böyle bir performans sorunu yaşadınız mı? Intersection Observer dışında, React Virtual List veya farklı lazy loading kütüphaneleri deneyen oldu mu? Deneyimlerinizi yorumlarda paylaşırsanız çok sevinirim. Bir sonraki yazıda görüşmek üzere, kodla kalın!
Sorun şuydu: Kullanıcı sadece ilk ekranda gördüğü içerikle etkileşime geçmek istiyordu ama tarayıcı, sayfanın en altındaki yorum bileşeninin CSS'sini, JS'ini ve resimlerini de indirip işliyordu. Bu, özellikle mobilde ve düşük performanslı cihazlarda ciddi bir gecikmeye neden oluyordu. "Acaba tüm bu içeriği lazy loading ile mi ayırsam?" diye düşünürken, daha native ve modern bir çözüm olan Intersection Observer'ı keşfettim.
Kısaca, bu API bize bir DOM elementinin viewport (görünür alan) ile kesişimini izleme imkanı veriyor. Yani bir div, viewport'a girdiği veya çıktığı anda bizi haberdar edebiliyor. Bu özelliği kullanarak, bir component'ı sadece görünür olacağı zaman DOM'a inject edebilir veya render ettirebiliriz.
İşte React projemde kullandığım temel bir hook yapısı:
JavaScript:
import { useEffect, useRef, useState } from 'react';
const useLazyLoad = (options = {}) => {
const containerRef = useRef(null);
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver(([entry]) => {
// Element görünür olduğunda state'i güncelle
if (entry.isIntersecting) {
setIsVisible(true);
// Gerekirse gözlemi durdur (sadece bir kere yüklemek için)
observer.unobserve(entry.target);
}
}, options);
if (containerRef.current) {
observer.observe(containerRef.current);
}
return () => {
if (containerRef.current) {
observer.unobserve(containerRef.current);
}
};
}, [options]);
return [containerRef, isVisible];
};
export default useLazyLoad;
Artık elimde sihirli bir hook var. Ağır olan ve ilk görünümde gerekli olmayan her component'ı bu hook ile sarmalayabilirim. İşte bir ürün listesi component'ı örneği:
JavaScript:
import React, { Suspense } from 'react';
import useLazyLoad from './hooks/useLazyLoad';
import ProductCardSkeleton from './ProductCardSkeleton';
// Ağır bileşeni lazy import ediyoruz
const HeavyProductCard = React.lazy(() => import('./HeavyProductCard'));
const ProductList = ({ products }) => {
const [containerRef, isVisible] = useLazyLoad({
rootMargin: '100px', // Viewport'a 100px kala tetikle
});
return (
<div ref={containerRef}>
{isVisible ? (
<Suspense fallback={<ProductCardSkeleton />}>
{products.map(product => (
<HeavyProductCard key={product.id} data={product} />
))}
</Suspense>
) : (
// Henüz görünmüyorsa, yer tutucu (skeleton) göster
<ProductCardSkeleton count={3} />
)}
</div>
);
};
export default ProductList;
Bu yapıda, HeavyProductCard component'ı ve onun içerdiği tüm modüller (resimler, ikon kütüphaneleri vs.), kullanıcı o bölüme yaklaşana kadar yüklenmiyor. rootMargin ile biraz daha erken tetikleyip, yükleme sırasında oluşabilecek boşluk hissini de azaltabilirsiniz.
Bu tekniği sayfanın alt bölümlerindeki yorumlar, önerilen ürünler ve sosyal medya feed'leri gibi alanlara uyguladıktan sonra, ilk sayfa yükünde gözle görülür bir iyileşme oldu. First Contentful Paint (FCP) ve Largest Contentful Paint (LCP) metriklerim ciddi oranda düzeldi. En güzeli de, kullanıcı deneyiminden hiç ödün vermemiş olmam. Onlar kaydırdıkça içerik sihirli bir şekilde beliriyor.
Siz de projelerinizde böyle bir performans sorunu yaşadınız mı? Intersection Observer dışında, React Virtual List veya farklı lazy loading kütüphaneleri deneyen oldu mu? Deneyimlerinizi yorumlarda paylaşırsanız çok sevinirim. Bir sonraki yazıda görüşmek üzere, kodla kalın!