Gezinme ön yükleme özelliği, paralel olarak istek göndererek servis çalışanı başlatma süresini azaltmanıza olanak tanır.
Özet
- Bazı durumlarda hizmet çalışanının başlatma süresi ağ yanıtını geciktirebilir.
- Üç büyük tarayıcı motorunda bulunan gezinme önyüklemesi, isteği hizmet çalışanı başlatılmasına paralel olarak göndermenize olanak tanıyarak bu sorunu giderir.
- Bir başlık kullanarak ön yükleme isteklerini normal gezinmelerden ayırt edebilir ve farklı içerikler yayınlayabilirsiniz.
Sorun
Getirme etkinliklerini işlemek için hizmet çalışanı kullanan bir siteye gittiğinizde tarayıcı, hizmet çalışanından yanıt ister. Bu işlem, hizmet çalışanını (çalışıyor değilse) başlatmayı ve getirme etkinliğini dağıtmayı içerir.
Önyükleme süresi cihaza ve koşullara bağlıdır. Bu süre genellikle 50 ms civarındadır. Mobil cihazlarda bu süre 250 ms'ye yakındır. Aşırı durumlarda (yavaş cihazlar, CPU'da sorun) bu süre 500 ms'nin üzerinde olabilir. Ancak hizmet çalışanı, etkinlikler arasında tarayıcı tarafından belirlenen bir süre boyunca etkin durumda kaldığından bu gecikmeyi yalnızca ara sıra görürsünüz (ör. kullanıcı yeni bir sekmeden veya başka bir siteden sitenize gittiğinde).
Ağı atlamanın avantajı, önyükleme gecikmesinden daha fazla olduğundan, önyükleme süresi, önbellekten yanıt veriyorsanız sorun teşkil etmez. 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 işçilerini atlayarak, hizmet işçilerini tahmini olarak başlatarak ve diğer optimizasyonlarla başlatma süresini azaltmaya devam ediyoruz. Ancak önyükleme süresi her zaman sıfırdan büyük olur.
Facebook, bu sorunun etkilerini bize bildirdi ve gezinme isteklerini paralel olarak gerçekleştirmenin bir yolunu istedi:
Navigasyon ön yükleme özelliğinden yararlanın
Gezinme önyüklemesi, "Kullanıcı bir GET gezinme isteği yaptığında, hizmet çalışanı başlatılırken ağ isteğini başlat" demenize olanak tanıyan bir özelliktir.
Başlangıç gecikmesi devam eder ancak ağ isteğini engellemediği için kullanıcı içeriği daha erken alır.
Bu işlemin çalışırken gösterildiği bir videoyu aşağıda bulabilirsiniz. Bu videoda, hizmet çalışanına while döngüsü kullanılarak kasıtlı olarak 500 ms başlangıç gecikmesi uygulanmaktadır:
Demoyu buradan izleyebilirsiniz. Gezinme ön yüklemesinin avantajlarından yararlanmak için bu özelliği destekleyen bir tarayıcıya ihtiyacınız vardır.
Gezinme ön yüklemesini etkinleştirme
addEventListener('activate', event => {
event.waitUntil(async function() {
// Feature-detect
if (self.registration.navigationPreload) {
// Enable navigation preloads!
await self.registration.navigationPreload.enable();
}
}());
});
navigationPreload.enable()
'ü istediğiniz zaman çağırabilir veya navigationPreload.disable()
ile devre dışı bırakabilirsiniz. Ancak fetch
etkinliğinizin bu özelliği kullanması gerektiğinden, en iyi yöntem bu özelliği hizmet çalışanınızın activate
etkinliğinde etkinleştirmek ve devre dışı bırakmaktır.
Önceden yüklenmiş yanıtı kullanma
Artık tarayıcı, gezinme için önceden getirme işlemi gerçekleştirecek olsa da yanıtı kullanma 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
, aşağıdaki durumlarda yanıtla çözülen bir sözdür:
- Gezinme önceden yükleme etkin.
- İstek bir
GET
isteğidir. - İstek, bir gezinme isteğidir (tarayıcılar, iframe'ler dahil sayfaları yüklerken oluşturur).
Aksi takdirde event.preloadResponse
hâlâ mevcuttur ancak undefined
ile çözümlenir.
Ön yüklemeler için özel yanıtlar
Sayfanızın ağdan veri alması gerekiyorsa en hızlı yol, servis çalışanında istek gönderip önbellekten ve ağdan alınan parçaları içeren tek bir akışlı yanıt oluşturmaktır.
Bir makale göstermek 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 mergeResponses
, her isteğin akışlarını birleştiren küçük bir işlevdir. Bu sayede, ağ içeriği aktarılırken önbelleğe alınmış başlığı gösterebiliriz.
Ağ isteği sayfa isteğiyle birlikte yapıldığı ve içerik önemli düzeyde müdahale olmadan aktarılabileceği için bu yöntem "uygulama kabuğu" modelinden daha hızlıdır.
Ancak includeURL
isteği, hizmet çalışanının başlatma süresi nedeniyle gecikecektir. Bu sorunu düzeltmek için gezinme ön yüklemesini de kullanabiliriz ancak bu durumda sayfanın tamamını değil, bir dahil etmeyi ön yüklemek istiyoruz.
Bunu desteklemek için her ön yükleme isteğiyle birlikte bir başlık gönderilir:
Service-Worker-Navigation-Preload: true
Sunucu, gezinme ön yükleme istekleri için normal bir gezinme isteğinde göndereceğinden farklı içerik göndermek amacıyla bunu kullanabilir. Önbellekleri 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
Service-Worker-Navigation-Preload
başlığının varsayılan değeri true
'dur ancak istediğiniz değere ayarlayabilirsiniz:
navigator.serviceWorker.ready.then(registration => {
return registration.navigationPreload.setHeaderValue(newValue);
}).then(() => {
console.log('Done!');
});
Örneğin, sunucunun yalnızca daha yeni veriler döndürmesi için bu değeri yerel olarak önbelleğe aldığınız son yayının kimliğine ayarlayabilirsiniz.
Durumu alma
getState
'ü kullanarak navigasyon ön yüklemesinin durumunu arayabilirsiniz:
navigator.serviceWorker.ready.then(registration => {
return registration.navigationPreload.getState();
}).then(state => {
console.log(state.enabled); // boolean
console.log(state.headerValue); // string
});
Bu özellik üzerinde yaptıkları çalışmalar ve bu makale için verdikleri destek için Matt Falkenhagen ve Tsuyoshi Horo'ya çok teşekkür ederiz. Standartlaştırma çalışmalarına katılan herkese çok teşekkür ederiz.