Gezinme ön yüklemesi, paralel istekler göndererek hizmet çalışanı başlatma süresinin üstesinden gelmenize olanak tanır.
Özet
- Bazı durumlarda hizmet çalışanı başlatma süresi, ağ yanıtını geciktirebilir.
- Üç ana tarayıcı motorunda mevcut olan gezinme ön yükleme özelliği, hizmeti Service Worker'ın başlatılmasına paralel olarak istek göndermenize olanak tanıyarak bu sorunu düzeltir.
- Bir başlık kullanarak, önceden yükleme isteklerini normal gezinmelerden ayırt edebilir ve farklı içerik sunabilirsiniz.
Sorun
Getirme etkinliklerini işlemek için Service Worker'ı kullanan bir siteye gittiğinizde tarayıcı, hizmet çalışanından yanıt ister. Bu işlem, hizmet çalışanının başlatılmasını (çalışmıyorsa) ve getirme etkinliğinin gönderilmesini içerir.
Başlatma süresi cihaza ve koşullara bağlıdır. Genellikle yaklaşık 50 ms. Mobil cihazlarda bu süre 250 ms'ye yakındır. Ekstrem durumlarda (yavaş cihazlar, sorun yaşayan CPU) bu değer 500 ms'nin üzerinde olabilir. Ancak, Service Worker etkinlikler arasında tarayıcıda belirlenen süre boyunca uyanık kaldığı için bu gecikmeyi yalnızca ara sıra (ör. kullanıcı yeni bir sekmeden veya başka bir siteden sitenize gittiğinde) alırsınız.
Önbellekten yanıt veriyorsanız başlatma süresi sorun yaratmaz, çünkü ağı atlamanın yararı, başlatma gecikmesinden daha uzundur. Ancak ağı kullanarak yanıt veriyorsanız...
Ağ isteği, hizmet çalışanının başlatılması nedeniyle gecikir.
V8'de kod önbelleğe alma özelliğini kullanarak, getirme etkinliği olmayan hizmet çalışanlarını atlayarak, service çalışanlarını tahmine dayalı olarak başlatarak ve diğer optimizasyonlarla yeniden başlatma süresini azaltmaya devam ediyoruz. Ancak önyükleme süresi her zaman sıfırdan büyük olur.
Facebook bu sorunun etkisini dikkatimize sundu ve buna paralel olarak navigasyon isteklerini yerine getirmenin bir yolunu istedi:
Navigasyon önceden yüklenerek kurtarılır
Gezinmeyi önceden yükleme özelliği, "Kullanıcı bir GET gezinme isteğinde bulunduğunda, hizmet çalışanı başlatılırken ağ isteğini başlat" demenize olanak tanıyan bir özelliktir.
Başlatma gecikmesi hâlâ söz konusudur, ancak ağ isteğini engellemez, bu nedenle kullanıcıya içerik daha kısa sürede ulaşır.
Aşağıda, bir zaman döngüsü kullanılarak hizmet çalışanına 500 ms'lik bir başlatma gecikmesinin verildiği uygulama videosunu bulabilirsiniz:
Demonun kendisi buradadır. Gezinmenin önceden yüklenmesinin avantajlarından yararlanmak için bunu destekleyen bir tarayıcıya ihtiyacınız vardır.
Gezinme ön yüklemesini etkinleştir
addEventListener('activate', event => {
event.waitUntil(async function() {
// Feature-detect
if (self.registration.navigationPreload) {
// Enable navigation preloads!
await self.registration.navigationPreload.enable();
}
}());
});
navigationPreload.enable()
numaralı telefonu istediğiniz zaman arayabilir veya navigationPreload.disable()
ile bu özelliği devre dışı bırakabilirsiniz. Ancak fetch
etkinliğinizin bu özellikten yararlanması gerektiği için en iyisi, Service Worker'ınızın activate
etkinliğinde bu özelliği etkinleştirip devre dışı bırakmaktır.
Önceden yüklenmiş yanıtı kullanma
Artık tarayıcı, gezinmeler için önceden yüklemeler gerçekleştirir ancak yanıtı kullanmanız gerekir:
addEventListener('fetch', event => {
event.respondWith(async function() {
// Respond from the cache if we can
const cachedResponse = await caches.match(event.request);
if (cachedResponse) return cachedResponse;
// Else, use the preloaded response, if it's there
const response = await event.preloadResponse;
if (response) return response;
// Else try the network.
return fetch(event.request);
}());
});
event.preloadResponse
, şu durumlarda yanıt olarak verilen bir vaattir:
- Gezinme ön yüklemesi etkin.
- İstek, bir
GET
isteği. - İstek, bir gezinme isteğidir (tarayıcıların, iframe'ler dahil olmak üzere sayfaları yüklerken oluşturduğu).
Aksi takdirde event.preloadResponse
hâlâ mevcuttur ancak undefined
ile çözümlenir.
Önceden yüklemeler için özel yanıtlar
Sayfanız için ağdan veri alınması gerekiyorsa en hızlı yöntem bu verileri hizmet çalışanından istemek ve önbellekten parçalar ile ağdan parçalar içeren tek bir akışlı yanıt oluşturmaktır.
Bir makale görüntülemek istediğimizi varsayalım:
addEventListener('fetch', event => {
const url = new URL(event.request.url);
const includeURL = new URL(url);
includeURL.pathname += 'include';
if (isArticleURL(url)) {
event.respondWith(async function() {
// We're going to build a single request from multiple parts.
const parts = [
// The top of the page.
caches.match('/article-top.include'),
// The primary content
fetch(includeURL)
// A fallback if the network fails.
.catch(() => caches.match('/article-offline.include')),
// The bottom of the page
caches.match('/article-bottom.include')
];
// Merge them all together.
const {done, response} = await mergeResponses(parts);
// Wait until the stream is complete.
event.waitUntil(done);
// Return the merged response.
return response;
}());
}
});
Yukarıdaki örnekte, her bir isteğin akışlarını birleştiren mergeResponses
küçük bir işlevdir. Bu, ağ içeriğinin akışı sırasında önbelleğe alınmış başlığı görüntüleyebileceğimiz anlamına gelir.
Bu yöntem, "uygulama kabuğu" modelinden daha hızlıdır. Çünkü ağ isteği, sayfa isteğiyle birlikte yapılır ve içerik, büyük saldırılar olmadan yayınlanabilir.
Ancak includeURL
isteği, hizmet çalışanının başlatma süresine göre ertelenir. Bunu düzeltmek için de gezinme ön yüklemesini kullanabiliriz, ancak bu durumda tam sayfayı önceden yüklemek istemeyiz ve bir dahil etme işlemini önceden yüklemek istiyoruz.
Bunu desteklemek için, her önceden yükleme isteğinde bir başlık gönderilir:
Service-Worker-Navigation-Preload: true
Sunucu, gezinme önceden yükleme istekleri için normal gezinme isteğinden farklı içerik göndermek üzere bunu kullanabilir. Önbelleklerin yanıtlarınızın farklı olduğunu bilmesi için Vary: Service-Worker-Navigation-Preload
üstbilgisi eklemeyi unutmayın.
Artık ön yükleme isteğini kullanabiliriz:
// Try to use the preload
const networkContent = Promise.resolve(event.preloadResponse)
// Else do a normal fetch
.then(r => r || fetch(includeURL))
// A fallback if the network fails.
.catch(() => caches.match('/article-offline.include'));
const parts = [
caches.match('/article-top.include'),
networkContent,
caches.match('/article-bottom')
];
Üstbilgiyi değiştirme
Varsayılan olarak, Service-Worker-Navigation-Preload
üst bilgisinin değeri true
şeklindedir, ancak istediğiniz gibi ayarlayabilirsiniz:
navigator.serviceWorker.ready.then(registration => {
return registration.navigationPreload.setHeaderValue(newValue);
}).then(() => {
console.log('Done!');
});
Örneğin, bunu yerel olarak önbelleğe aldığınız son yayının kimliğine ayarlayabilirsiniz. Böylece, sunucu yalnızca daha yeni verileri döndürür.
Durum bilgisi alınıyor
getState
kullanarak navigasyonun önceden yüklenmesinin durumunu arayabilirsiniz:
navigator.serviceWorker.ready.then(registration => {
return registration.navigationPreload.getState();
}).then(state => {
console.log(state.enabled); // boolean
console.log(state.headerValue); // string
});
Bu özellikle ilgili çalışmaları ve bu makaleyle ilgili yardımları için Matt Falkenhagen ve Tsuyoshi Horo'ya çok teşekkür ederiz. Standartlaştırma çalışmalarına dahil olan herkese çok teşekkür ederiz.