Gezinme önceden yüklemesi, istekleri paralel olarak göndererek hizmet çalışanı başlatma süresinin üstesinden gelmenizi sağlar.
Özet
- Bazı durumlarda Service Worker başlatma süresi, ağ yanıtını geciktirebilir.
- Üç ana tarayıcı motorunda kullanılabilir olan navigasyon önceden yüklemesi, isteği Service Worker önyüklemesiyle paralel olarak yapmanıza olanak tanıyarak bu sorunu düzeltir.
- Bir başlık kullanarak ön yükleme isteklerini normal gezinmelerden ayırt edebilir ve farklı içerikler sunabilirsiniz.
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ın başlatılmasını (zaten çalışmıyorsa) ve getirme etkinliğinin gönderilmesini içerir.
Açılma süresi cihaza ve koşullara göre değişir. Genellikle 50 ms civarındadır. Mobilde bu değer 250 ms'ye yakın. Olağanüstü durumlarda (yavaş cihazlar, sorunlu CPU) 500 ms'nin üzerinde olabilir. Ancak Service Worker, etkinlikler arasında tarayıcının belirlediği bir süre boyunca uyanık kaldığından bu gecikmeyi yalnızca ara sıra (ör. kullanıcı sitenize yeni bir sekmeden veya başka bir siteden geldiğinde) yaşarsınız.
Ağı atlamanın avantajı, başlatma gecikmesinden daha fazla olduğundan, önbellekten yanıt veriyorsanız başlatma süresi sorun teşkil etmez. Ancak ağ üzerinden 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, hizmet çalışanlarını tahmine dayalı şekilde başlatarak ve diğer optimizasyonları uygulayarak başlatma süresini azaltmaya devam ediyoruz. Ancak başlatma süresi her zaman sıfırdan büyük olur.
Facebook bu sorunun etkisini bize hatırlattı ve buna paralel olarak navigasyon isteklerini yerine getirmek için bir yöntem istedi:
Navigasyon kurtarmaya önceden yüklenir
Navigasyon önceden yüklemesi, "Kullanıcı bir GET gezinme isteği gönderdiğinde ağ isteğini Service Worker başlatılırken başlat" demenize olanak sağlayan bir özelliktir.
Başlatma gecikmesi devam eder ancak ağ isteğini engellemez, böylece kullanıcı içeriği daha erken alır.
Burada, hizmet çalışanına bir süre döngüsü kullanarak 500 ms başlatma gecikmesinin verildiği bir çalışma videosunu görebilirsiniz:
Demoyu burada bulabilirsiniz. Gezinmeyi önceden yüklemenin avantajlarından yararlanmak için bunu destekleyen bir tarayıcıya ihtiyacınız vardır.
Gezinmeyi önceden yüklemeyi 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()
hizmetini istediğiniz zaman arayabilir veya navigationPreload.disable()
ile devre dışı bırakabilirsiniz. Ancak fetch
etkinliğinizin bunu kullanması gerektiğinden hizmet çalışanınızın activate
etkinliğinde etkinliği etkinleştirip devre dışı bırakmanız en iyisidir.
Önceden yüklenmiş yanıtı kullanma
Artık tarayıcı gezinmeler için önceden yüklemeler gerçekleştirecektir, ancak yine de 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
, aşağıdaki durumlarda yanıt ile sonuçlanan bir taahhüttür:
- Navigasyonu önceden yükleme etkinleştirildi.
- İstek bir
GET
isteği. - İstek bir gezinme isteğidir (tarayıcılar, iframe'ler de dahil olmak üzere sayfaları yüklerken oluşturur).
Aksi takdirde event.preloadResponse
hâlâ orada kalır ancak undefined
ile çözümlenir.
Önceden yüklemeler için özel yanıtlar
Sayfanız için ağdan veri almanız gerekiyorsa en hızlı yöntem, bunu hizmet çalışanında istemek ve önbellekten bölümler ile ağdaki bölümleri 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ıda mergeResponses
her isteğin akışlarını birleştiren küçük bir işlevdir. Bu, ağ içeriği akışı sırasında önbelleğe alınmış üstbilgiyi görüntüleyebileceğimiz anlamına gelir.
Bu, "uygulama kabuğundan" daha hızlıdır modeli, sayfa isteğiyle birlikte yapılır ve içerik önemli saldırılar olmadan akış gerçekleştirebilir.
Ancak includeURL
isteği, hizmet çalışanının başlatma süresine göre gecikir. Bunu düzeltmek için gezinme önceden yüklenmesini de kullanabiliriz, ancak bu durumda tam sayfayı önceden yüklemek istemiyoruz ve bir eklemeyi önceden yüklemek istiyoruz.
Bunu desteklemek için her önceden 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ği için olduğundan 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.
Şimdi önceden 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
üstbilgisinin değeri varsayılan olarak true
olsa da bunu istediğiniz gibi ayarlayabilirsiniz:
navigator.serviceWorker.ready.then(registration => {
return registration.navigationPreload.setHeaderValue(newValue);
}).then(() => {
console.log('Done!');
});
Örneğin, bu değeri yerel olarak önbelleğe aldığınız son kaydın kimliğine ayarlayabilirsiniz. Böylece, sunucunun yalnızca yeni veriler döndürmesi sağlanır.
Durum öğreniliyor
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ından dolayı Matt Falkenhagen ve Tsuyoshi Horo'ya teşekkür ederiz. Bu makale, onlara yardımcı oldu. Standartlaştırma çabasında yer alan herkese çok teşekkür ederim.