Merhaba sevgili dostum,
React ve Next.js ile geliştirilen projelerde, özellikle mobil tarafta LCP (Largest Contentful Paint) ve FID (First Input Delay) değerlerinin bir türlü yeşile dönmemesi, inanın ki yalnızca sizin değil, sektördeki birçok tecrübeli geliştiricinin de uykusunu kaçıran bir sorun. Yaptığınız temel optimizasyonlar (resimler, lazy loading, CDN) doğru adımlar, ama dinamik ve JavaScript yoğun bir sitede işin derinliği bambaşka boyutlara ulaşabiliyor. Bu sorunun köklerine inelim ve size gerçek dünya tecrübelerimden süzülmüş, uygulanabilir çözümler sunalım.
Mobil Performans Neden Bu Kadar Kritik ve Zorlu?
Öncelikle, mobil kullanıcı deneyiminin neden bu kadar merkezi olduğunu bir kez daha hatırlayalım. Akıllı telefonlar artık çoğu internet trafiğinin kaynağı. Kullanıcılar hareket halindeyken, bazen zayıf bağlantılarla sitenize erişmeye çalışıyor. Onların beklentisi ise hızlı ve akıcı bir deneyim. LCP ve FID gibi metrikler de tam da bu anları ölçüyor: Sayfanın en büyük görsel içeriğinin ne zaman görünür olduğunu (LCP) ve kullanıcının sayfayla ilk etkileşiminin ne kadar geciktiğini (FID). Bu metrikler düşük olduğunda, kullanıcılar sıkılıp sitenizden ayrılıyorlar.
Next.js gibi güçlü bir çerçeve kullanmamıza rağmen, dinamik ve interaktif içerik sunarken karşılaştığımız zorluklar kaçınılmaz olabiliyor. Zira, tarayıcının sadece içeriği yüklemesi yetmiyor; aynı zamanda JavaScript'i işleyip sayfayı "canlandırması" (hydration) gerekiyor ki kullanıcı etkileşime geçebilsin. İşte bu noktada genellikle sıkıntı başlıyor.
Temel Ötesi Adımlar: JavaScript'i Akıllıca Yönetmek
Temel optimizasyonları yaptığınızı varsayarak, şimdi daha derinlere inelim. Özellikle JavaScript yoğun siteler için şu adımlar hayat kurtarıcı olabilir:
1. JavaScript Yükünü Hafifletin ve Akıllıca Parçalayın (Code Splitting)
Bu belki de en önemli adımdır. Birçok Next.js projesinde, farkında olmadan tüm sayfalar için gerekli olan JavaScript'i her sayfada yüklüyoruz.
Dinamik İçe Aktarımlar (Dynamic Imports): Next.js'in en güçlü özelliklerinden biri next/dynamic ile komponentleri sadece ihtiyaç duyulduğunda yüklemektir.
* Örnek: Sayfanızın alt kısmında kalan, hemen görünmeyen bir yorum bölümü veya karmaşık bir harita komponenti mi var? Bu komponentleri dinamik olarak içe aktarın.
`javascript
import dynamic from 'next/dynamic';
const DynamicCommentSection = dynamic(() => import('../components/CommentSection'), {
ssr: false, // Bu bölümün sadece client tarafında renderlanmasını istiyorsanız
loading: () =>
Yorumlar yükleniyor...
, // Yüklenirken gösterecek bir placeholder
});
// Kullanım
`
Bu sayede, kullanıcı o bölüme kaydırmadıkça veya tıklamadıkça ilgili JavaScript yüklenmez, ilk yükleme süresi ve FID için kritik olan ana thread meşguliyeti azalır.
Kullanılmayan Kodları Temizleyin (Tree-shaking'in Ötesinde): Bazen projemize eklediğimiz bazı kütüphanelerin sadece küçük bir kısmını kullanırız. Webpack'in "tree-shaking" özelliği bir yere kadar yardımcı olsa da, siz de gereksiz gördüğünüz, artık kullanılmayan kütüphaneleri veya komponentleri projenizden temizlemelisiniz. Bu, hem paket boyutunu küçültür hem de JavaScript'in çalışma zamanı maliyetini azaltır.
2. Üçüncü Parti Script Yönetimi: Sessiz Katiller
Reklamlar, analiz araçları (Google Analytics, Hotjar), sohbet botları, A/B test araçları... Bu üçüncü parti scriptler sitenize zenginlik katarken, performansa en büyük darbelerden birini vurabilir. Genellikle senkronize yüklendikleri için sayfanızın render edilmesini engelleyebilir ve ana thread'i bloke ederek FID'yi tavana vurabilirler.
next/script Bileşenini Kullanın: Next.js'in next/script bileşeni, üçüncü parti scriptleri optimize etmek için harika bir yoldur.
`javascript
import Script from 'next/script';
function MyApp({ Component, pageProps }) {
return (
<>
<Script
src="https://www.googletagmanager.com/gtag/js?id=YOUR_GA_ID"
strategy="afterInteractive" // Sayfa etkileşimli hale geldikten sonra yükle
/>
<Script id="google-analytics">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'YOUR_GA_ID');
`}
</Script>
<Component {...pageProps} />
</>
);
}
`
strategy özelliği ile script'in ne zaman yükleneceğini belirleyebilirsiniz:
beforeInteractive: En erken yüklenir, dikkatli kullanılmalı.
afterInteractive: Sayfa etkileşimli hale geldikten sonra yüklenir. Çoğu analiz ve reklam scripti için idealdir.
* lazyOnload: Tarayıcı boşta kaldığında yüklenir. Sohbet botları veya widget'lar için harika.
Gereksiz Scriptleri Erteleyin veya Kaldırın: Gerçekten ihtiyacınız olmayan bir script var mı? Yoksa yükleme önceliği değiştirebilir misiniz? Örneğin, bir sohbet botu widget'ı sayfanın hemen görünmesi için kritik değilse, lazyOnload stratejisiyle yükleyin.
3. Kritik Render Path Optimizasyonu: LCP'ye Dokunuş
LCP, sayfanızın en büyük içeriğinin ne kadar sürede yüklendiğini ölçer. Bu genellikle bir resim, bir video veya büyük bir metin bloğu olabilir.
- LCP Elementini Tespit Edin: Lighthouse size LCP elementini gösterecektir. Bu elementi hızlandırmak için özel önlemler alın.
Öncelikli Resimler için next/image ve priority: Eğer LCP elementiniz bir görsel ise, next/image kullanın ve priority özelliğini true yapın. Bu, Next.js'e bu resmin kritik olduğunu söyler ve tarayıcıya daha erken yüklemesi için ipuçları verir.
`javascript
import Image from 'next/image';
<Image
src="/hero-image.jpg"
alt="Ana görsel"
width={1200}
height={800}
priority // Bu resim LCP için kritik!
/>
`
- Kritik CSS'i Satır İçi Yapma (Inlining Critical CSS): Sayfanın ilk görünümü için gerekli olan CSS'i doğrudan HTML içine gömmek (inlining), tarayıcının ayrı bir CSS dosyası indirmesini beklemesini engeller. Next.js, CSS modülleri ve styled-components gibi çözümlerle bunu bir ölçüde zaten yapabilir. Ancak, daha büyük stiller için sadece ilk görünüm (above-the-fold) için gerekli olan CSS'i belirleyip inlining yapmak faydalı olabilir.
Yazı Tiplerini Optimize Edin (next/font): Özel yazı tipleri de LCP'yi geciktirebilir. next/font modülü, yazı tiplerini otomatik olarak optimize eder ve "layout shift" (CLS) sorunlarını en aza indirmeye yardımcı olur.
`javascript
import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'] });
export default function RootLayout({ children }) {
return (
<html lang="tr" className={inter.className}>
<body>{children}</body>
</html>
);
}
`
Bu yöntem, yazı tiplerinin yüklenme şeklini en optimize hale getirir.
4. Sunucu Tarafı Renderlama (SSR) ve Statik Site Üretimi (SSG) Arasında Doğru Denge
Next.js'in gücü, farklı veri getirme stratejileri sunmasıdır. Her sayfanın mutlaka getServerSideProps ile sunucu tarafında işlenmesi gerekmez.
- Statik Üretilebilecek Sayfaları Belirleyin: Eğer bir sayfanın içeriği sık değişmiyorsa,
getStaticProps kullanarak statik olarak üretin. Bu sayfalar bir CDN üzerinden çok hızlı servis edilir. Dinamik kısmı ise client tarafında fetch edin.
- Örnek: Bir blog yazısı veya sabit bir hakkımızda sayfası için
getStaticProps kullanın. Eğer çok dinamik bir içerikse ama anlık güncellenmesi gerekmiyorsa, getStaticProps ile revalidate özelliğini kullanın.
- Edge Caching'i Kullanın: Next.js'i Vercel gibi bir platformda barındırıyorsanız, bu platformlar Edge Network'ler aracılığıyla içeriğinizi kullanıcılara coğrafi olarak yakın sunuculardan ulaştırır. Bu, hem SSR hem de SSG sayfalarınız için kritik bir hızlandırma sağlar. Sunucu yanıt süresi (TTFB) de LCP üzerinde doğrudan etkilidir.
Gerçek Hayattan Bir Kesit: Bir E-ticaret Sitesi Örneği
Bir keresinde, geliştirdiğimiz yoğun ürün detay sayfalarına sahip bir e-ticaret sitesinde benzer bir sorun yaşamıştık. Mobil LCP 5-6 saniyeleri buluyordu. Temel resim optimizasyonları ve CDN kullanılmasına rağmen, sorun devam ediyordu. Derinlemesine incelediğimizde:
- Gereksiz Üçüncü Parti Scriptler: Sayfada anket, pop-up, dinamik öneri ve sohbet botu gibi birçok script vardı. Bunların çoğunu
next/script ile afterInteractive veya lazyOnload stratejisiyle yükleyerek ilk yükleme süresini ciddi oranda düşürdük. Özellikle sohbet botu, sayfanın ana içeriğinden önce yüklenmeye çalışarak LCP ve FID'yi mahvediyordu.
- LCP Elementine Odaklanma: Ürün görseli, LCP elementiydi.
next/image bileşeninde priority özelliğini kullanmak, tarayıcının bu resmi çok daha erken yüklemesini sağladı. Ayrıca, bu resmin doğru boyutlandırıldığından ve modern formatlarda (WebP, AVIF) servis edildiğinden emin olduk.
- Dinamik Komponent Yükleme: Ürün açıklamalarının altındaki "müşteri yorumları" ve "benzer ürünler" kısımlarını
dynamic(() => import(...)) ile sadece kullanıcı o bölüme kaydırdığında yüklenecek şekilde ayarladık. Bu, hem ilk sayfa yükleme boyutunu hem de ana thread üzerindeki JavaScript işlem yükünü azalttı.
Bu adımlar sonrasında mobil LCP değerimiz 2 saniyenin altına indi, FID ise neredeyse sıfıra yaklaştı. Kullanıcı deneyimi hissedilir derecede iyileşti.
Sürekli Ölçüm ve İyileştirme: Yolculuk Devam Ediyor
Unutmayın, performans optimizasyonu tek seferlik bir iş değildir. Siteniz geliştikçe, yeni özellikler eklendikçe veya üçüncü parti scriptler güncellendikçe performans düşüşleri yaşanabilir.
- Gerçek Kullanıcı İzleme (RUM): Lighthouse ve PageSpeed Insights laboratuvar verileri sunar. Asıl önemli olan, gerçek kullanıcılarınızın deneyimidir (Field Data). Google Search Console, Web Vitals raporları ile size bu bilgiyi sunar. Bu verilere düzenli olarak bakın.
- Otomatik Testler: CI/CD süreçlerinize performans testlerini entegre edin. Her yeni sürümde performansın düşmediğinden emin olun.
- Mobil Öncelik: Tüm testlerinizi ve optimizasyonlarınızı mobil cihazda yapın. Masaüstü performansı iyi olabilirken, mobil taraf farklılık gösterebilir.
Bu derinlemesine optimizasyon teknikleriyle, Next.js projenizin mobil performansını zirveye taşıyacağınıza eminim. Adım adım ilerleyin, her değişikliğin etkisini ölçün ve en önemlisi, sabırlı olun. Başarı kesinlikle sizinle olacaktır!