Merhaba arkadaşlar, bugün sizlere kullanıcı deneyimini uçtan uca değiştiren bir optimizasyon hikayesinden bahsedeceğim. Projemizde, kullanıcıların tekrar ziyaretlerinde sayfaların yeniden yüklenmesini beklemekten şikayet ettiğini fark ettik. Özellikle mobilde zayıf ağlarda, her seferinde tüm asset'lerin (CSS, JS, görseller) indirilmesi can sıkıcı bir gecikmeye neden oluyordu. İşte o zaman Service Worker ile offline önbellekleme yapmaya karar verdim ve sonuçlar inanılmazdı!
Karşılaştığım Temel Sorun
Sorun şuydu: Kullanıcı sitemizi bir kez ziyaret ettiğinde, tarayıcı normal cache kurallarına uysa da, sayfayı tamamen "offline" modda açamıyordu. Sayfa iskeleti (HTML) geliyor ama stil ve scriptler yüklenemeyince beyaz bir ekran veya bozuk bir arayüzle karşılaşılıyordu. Amacım, ilk ziyaretten sonraki tüm ziyaretlerde, sayfanın ağ durumundan bağımsız olarak anında ve tam olarak açılmasını sağlamaktı.
Service Worker'ı Kayıt Etmek
İlk adım, basit bir JavaScript dosyası ile Service Worker'ı kaydetmek. Bu işlemi ana uygulama JavaScript dosyanızın başında veya HTML'nizin sonunda yapabilirsiniz.
Bu kod, tarayıcının Service Worker API'sini destekleyip desteklemediğini kontrol eder ve sayfa yüklendikten sonra '/sw.js' dosyasını kaydeder.
Önbellekleme Stratejisi: Cache First + Network Fallback
Asıl sihir, 'sw.js' dosyasında gerçekleşiyor. Benim tercihim, kritik asset'ler için Cache First, diğer istekler için ise Network First veya Stale-While-Revalidate stratejilerini kullanmak oldu. İşte temel önbellekleme mantığı:
Fetch Olayını Yakalayıp Cevap Vermek
Service Worker kurulduktan sonra, tüm ağ isteklerini araya girip yönetebilir. Aşağıdaki kod, öncelikle önbellekte (cache) sonucu arar, bulamazsa ağa (network) gider. Ağdan gelen cevabı da bir sonraki kullanım için önbelleğe alır.
Eski Cache'leri Temizlemek
Uygulamanızı güncellediğinizde, eski cache'lerin kalmasını istemezsiniz. 'activate' olayında, yeni cache versiyonunuz dışındaki her şeyi silmelisiniz.
Bu yöntemi uyguladıktan sonra, kullanıcıların ikinci ve sonraki ziyaretlerinde sayfaların neredeyse anında açıldığını gözlemledik. Lighthouse performans skorlarında ciddi bir artış oldu, özellikle First Contentful Paint ve Speed Index metrikleri mükemmel seviyelere ulaştı.
Peki siz daha önce Service Worker ile offline deneyimleri geliştirdiniz mi? Cache First stratejisi sizin için sorun yarattı mı, yoksa farklı bir strateji (Network First, Stale-While-Revalidate) mi kullanıyorsunuz? Dinamik içerikli sayfalarda (API istekleri) bu yapıyı nasıl yönettiniz? Yorumlarda deneyimlerinizi paylaşın, tartışalım!
Sorun şuydu: Kullanıcı sitemizi bir kez ziyaret ettiğinde, tarayıcı normal cache kurallarına uysa da, sayfayı tamamen "offline" modda açamıyordu. Sayfa iskeleti (HTML) geliyor ama stil ve scriptler yüklenemeyince beyaz bir ekran veya bozuk bir arayüzle karşılaşılıyordu. Amacım, ilk ziyaretten sonraki tüm ziyaretlerde, sayfanın ağ durumundan bağımsız olarak anında ve tam olarak açılmasını sağlamaktı.
İlk adım, basit bir JavaScript dosyası ile Service Worker'ı kaydetmek. Bu işlemi ana uygulama JavaScript dosyanızın başında veya HTML'nizin sonunda yapabilirsiniz.
JavaScript:
// app.js veya main.js içinde
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js').then(function(registration) {
console.log('ServiceWorker kaydı başarılı: ', registration.scope);
}).catch(function(err) {
console.log('ServiceWorker kaydı başarısız: ', err);
});
});
}
Bu kod, tarayıcının Service Worker API'sini destekleyip desteklemediğini kontrol eder ve sayfa yüklendikten sonra '/sw.js' dosyasını kaydeder.
Asıl sihir, 'sw.js' dosyasında gerçekleşiyor. Benim tercihim, kritik asset'ler için Cache First, diğer istekler için ise Network First veya Stale-While-Revalidate stratejilerini kullanmak oldu. İşte temel önbellekleme mantığı:
JavaScript:
// sw.js - Service Worker dosyası
const CACHE_NAME = 'site-static-v1';
const ASSETS_TO_CACHE = [
'/',
'/index.html',
'/styles/main.min.css',
'/scripts/app.bundle.js',
'/images/logo.svg',
'https://fonts.googleapis.com/css?family=Roboto'
];
// Kurulum Aşaması: Asset'leri Önbelleğe Al
self.addEventListener('install', event => {
console.log('[Service Worker] Kurulum');
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
console.log('[Service Worker] Kritik dosyalar önbelleğe alınıyor');
return cache.addAll(ASSETS_TO_CACHE);
})
.then(() => self.skipWaiting()) // Yeni SW hemen aktif olur
);
});
Service Worker kurulduktan sonra, tüm ağ isteklerini araya girip yönetebilir. Aşağıdaki kod, öncelikle önbellekte (cache) sonucu arar, bulamazsa ağa (network) gider. Ağdan gelen cevabı da bir sonraki kullanım için önbelleğe alır.
JavaScript:
// sw.js devamı
self.addEventListener('fetch', event => {
console.log('[Service Worker] Fetch ediliyor: ', event.request.url);
event.respondWith(
caches.match(event.request)
.then(cacheResponse => {
// Önbellekte varsa, onu döndür.
if (cacheResponse) {
return cacheResponse;
}
// Yoksa, ağ isteği yap.
return fetch(event.request).then(networkResponse => {
// Geçerli bir cevap değilse, olduğu gibi döndür.
if(!networkResponse || networkResponse.status !== 200 || networkResponse.type !== 'basic') {
return networkResponse;
}
// Cevabı klonla (stream sadece bir kez okunabilir)
const responseToCache = networkResponse.clone();
caches.open(CACHE_NAME)
.then(cache => {
cache.put(event.request, responseToCache);
});
return networkResponse;
});
}).catch(() => {
// Hem cache hem network hatası (offline durumu).
// Örneğin, offline sayfası gösterebilirsiniz.
// return caches.match('/offline.html');
})
);
});
Uygulamanızı güncellediğinizde, eski cache'lerin kalmasını istemezsiniz. 'activate' olayında, yeni cache versiyonunuz dışındaki her şeyi silmelisiniz.
JavaScript:
// sw.js devamı
self.addEventListener('activate', event => {
console.log('[Service Worker] Aktif');
// Eski cache'leri temizle
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cache => {
if (cache !== CACHE_NAME) {
console.log('[Service Worker] Eski cache temizleniyor: ', cache);
return caches.delete(cache);
}
})
);
}).then(() => self.clients.claim()) // Kontrolü tüm istemcilere hemen devret
);
});
Bu yöntemi uyguladıktan sonra, kullanıcıların ikinci ve sonraki ziyaretlerinde sayfaların neredeyse anında açıldığını gözlemledik. Lighthouse performans skorlarında ciddi bir artış oldu, özellikle First Contentful Paint ve Speed Index metrikleri mükemmel seviyelere ulaştı.
Peki siz daha önce Service Worker ile offline deneyimleri geliştirdiniz mi? Cache First stratejisi sizin için sorun yarattı mı, yoksa farklı bir strateji (Network First, Stale-While-Revalidate) mi kullanıyorsunuz? Dinamik içerikli sayfalarda (API istekleri) bu yapıyı nasıl yönettiniz? Yorumlarda deneyimlerinizi paylaşın, tartışalım!