Я новичок в ServiceWorkers, но я хотел сохранить мою домашнюю страницу как можно более свободной sh, используя кэш, а затем сетевой шаблон, как показано в автономной кулинарной книге Google .
Однако вместо потоков данных я использую stati c html, а исходный код, связанный с документацией Google, не использует этот пример. Итак, я разработал шаблон, который работает в тестировании, но я обеспокоен тем, что он вводит условие гонки.
Шаблон, который я пытаюсь достичь:
- Запрос
- ServiceWorker возвращает кэшированный ответ
- Страница отправляет сообщение в ServiceWorker с указанием использовать сеть при следующей загрузке
- Страница делает
fetch
запрос - Обновления ServiceWorker кеширует и возвращает сетевой ответ
- Обновления страницы с сетевым ответом
Однако может быть условие гонки между 3 и 4 .
Проблема в том, что 3 и 4 происходят в клиенте, в то время как 5 происходит в ServiceWorker.
Я не уверен в потенциальной задержке, вызванной postMessage()
. Если это существенно, 4 будет извлекать страницу до того, как ServiceWorker узнает, что она должна извлекаться из сети, и будет возвращен повторный кэшированный ответ.
Это так? Или у ServiceWorker всегда будет время обновить флаг с postMessage()
до следующего запроса на выборку?
Source
Page
let isControlled = navigator.serviceWorker.controller;
let isHomepage = location.href === location.origin + '/';
let homepageCached = getCookie('homecached') === 'true';
if (isControlled && !homepageCached) {
// If the cookie is expired or unset, push an update to the service worker and update cookie
navigator.serviceWorker.controller.postMessage('updateHomepageCache');
// `1` means store the cookie for 1 day
setCookie('homecached', 'true', 1);
if (isHomepage) {
// This works easily with location.reload(), but it's sloppy and causes a flicker
let existingMain = document.querySelector('.main');
fetch(location.href)
.then(function(response) {
return response.text();
}).then(function(text) {
try {
// Convert response text to a new document
let parser = new DOMParser();
let doc = parser.parseFromString(text, 'text/html');
// Get `main` element of response and update the current document with it
let fetchedMain = doc.querySelector('.main');
let parent = existingMain.parentNode;
parent.replaceChild(fetchedMain, existingMain);
} catch (err) {
console.error(err);
}
});
}
}
Сервисный работник
// Set flag to false so update only happens when cache is invalid
let updateHomepageCache = false;
self.addEventListener('message', (event) => {
// Update flag if cache is invalid
// Note, this comes after fetch in the ServiceWorker's lifecycle
if (event.data === 'updateHomepageCache') {
updateHomepageCache = true;
}
});
self.addEventListener('fetch', (event) => {
const normalizedUrl = new URL(event.request.url);
normalizedUrl.search = '';
const isNavgation = event.request.mode === 'navigate';
const isFromOrigin = normalizedUrl.origin === location.origin;
const isHomepage = normalizedUrl.href === location.origin + '/';
// Respond with network and update cache for homepage (if it needs to be updated)
if (isHomepage && isNavgation && updateHomepageCache) {
event.respondWith(
caches.open(cacheName).then(function(cache) {
return fetch(normalizedUrl).then(function(networkResponse) {
cache.put(normalizedUrl, networkResponse.clone());
updateHomepageCache = false;
return networkResponse;
}).catch(function() {
return cache.match(normalizedUrl);
});
})
);
// Cache then update cache with network response for pages "stale-while-revalidate"
} else if (isFromOrigin && isNavgation) {
event.respondWith(
caches.open(cacheName).then(function(cache) {
return cache.match(normalizedUrl).then(function(response) {
let fetchPromise = fetch(normalizedUrl).then(function(networkResponse) {
cache.put(normalizedUrl, networkResponse.clone());
return networkResponse;
});
return response || fetchPromise;
});
})
);
});