我一直在阅读《html5rocks服务工作者简介》一文,并创建了一个基本的服务工作者,它可以缓存页面、JS和CSS,并按预期工作:
var CACHE_NAME = 'my-site-cache-v1';
var urlsToCache = [
'/'
];
// Set the callback for the install step
self.addEventListener('install', function (event) {
// Perform install steps
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
});
self.addEventListener('fetch', function (event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
// Cache hit - return response
if (response) {
return response;
}
// IMPORTANT: Clone the request. A request is a stream and
// can only be consumed once. Since we are consuming this
// once by cache and once by the browser for fetch, we need
// to clone the response
var fetchRequest = event.request.clone();
return fetch(fetchRequest).then(
function(response) {
// Check if we received a valid response
if(!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
// IMPORTANT: Clone the response. A response is a stream
// and because we want the browser to consume the response
// as well as the cache consuming the response, we need
// to clone it so we have 2 stream.
var responseToCache = response.clone();
caches.open(CACHE_NAME)
.then(function(cache) {
cache.put(event.request, responseToCache);
});
return response;
}
);
})
);
});
当我对CSS进行更改时,由于服务人员正在从缓存中正确返回CSS,因此不会拾取此更改。
我陷入困境的地方是,如果我要更改HTML、JS或CSS,我如何确保服务人员可以从服务器加载较新版本,而不是从缓存加载?我曾尝试在CSS导入上使用版本戳,但似乎不起作用。
这里的主要问题是,当您的新服务工作人员安装时,他获取由前一个服务工作人员处理的请求,很可能他从缓存中获取资源,因为这是您的缓存策略。然后,即使您正在用新代码更新您的服务工作者,一个新的缓存名称,调用self.skip等待()
,他仍然将缓存中的旧资源放入缓存中!
需要知道的一件事是,每次代码脚本更改时,服务人员都会触发安装事件,因此您不需要使用版本戳或其他任何东西,只需保持相同的文件名就可以了,甚至建议您这样做。浏览器还可以考虑其他方式来更新服务人员。
1.重写安装事件处理程序:
我不使用cache.addAll
,因为它坏了。事实上,如果一个和只有一个你的资源缓存不能被提取,整个安装将失败,甚至没有一个单一的文件将被添加到缓存。现在想象一下,你要缓存的文件列表是从一个桶中自动生成的(这是我的情况),你的桶被更新,一个文件被删除,那么你的PWA将无法安装,它不应该。
西南。js
self.addEventListener('install', (event) => {
// prevents the waiting, meaning the service worker activates
// as soon as it's finished installing
// NOTE: don't use this if you don't want your sw to control pages
// that were loaded with an older version
self.skipWaiting();
event.waitUntil((async () => {
try {
// self.cacheName and self.contentToCache are imported via a script
const cache = await caches.open(self.cacheName);
const total = self.contentToCache.length;
let installed = 0;
await Promise.all(self.contentToCache.map(async (url) => {
let controller;
try {
controller = new AbortController();
const { signal } = controller;
// the cache option set to reload will force the browser to
// request any of these resources via the network,
// which avoids caching older files again
const req = new Request(url, { cache: 'reload' });
const res = await fetch(req, { signal });
if (res && res.status === 200) {
await cache.put(req, res.clone());
installed += 1;
} else {
console.info(`unable to fetch ${url} (${res.status})`);
}
} catch (e) {
console.info(`unable to fetch ${url}, ${e.message}`);
// abort request in any case
controller.abort();
}
}));
if (installed === total) {
console.info(`application successfully installed (${installed}/${total} files added in cache)`);
} else {
console.info(`application partially installed (${installed}/${total} files added in cache)`);
}
} catch (e) {
console.error(`unable to install application, ${e.message}`);
}
})());
});
2.激活(新)服务辅助程序时清理旧缓存:
西南。js
// remove old cache if any
self.addEventListener('activate', (event) => {
event.waitUntil((async () => {
const cacheNames = await caches.keys();
await Promise.all(cacheNames.map(async (cacheName) => {
if (self.cacheName !== cacheName) {
await caches.delete(cacheName);
}
}));
})());
});
3.每次更新资产时,我都会更新缓存名称:
西南。js
// this imported script has the newly generated cache name (self.cacheName)
// and a list of all the files on my bucket I want to be cached (self.contentToCache),
// and is automatically generated in Gitlab based on the tag version
self.importScripts('cache.js');
// the install event will be triggered if there's any update,
// a new cache will be created (see 1.) and the old one deleted (see 2.)
4.在缓存中处理过期
和缓存控制
响应头
我在服务工作者的fetch事件处理程序中使用这些头来捕获当资源过期/应该刷新时,它是否应该通过网络请求资源。
基本示例:
// ...
try {
const cachedResponse = await caches.match(event.request);
if (exists(cachedResponse)) {
const expiredDate = new Date(cachedResponse.headers.get('Expires'));
if (expiredDate.toString() !== 'Invalid Date' && new Date() <= expiredDate) {
return cachedResponse.clone();
}
}
// expired or not in cache, request via network...
} catch (e) {
// do something...
}
// ...
使缓存无效的一种方法是,每当您更改缓存文件中的任何内容时,都会对cache\u NAME
的版本进行修改。因为该更改将更改服务人员。js
浏览器将加载一个新版本,您将有机会删除旧缓存并创建新缓存。您可以在激活
处理程序中删除旧缓存。这是预取示例中描述的策略。如果您已经在CSS文件上使用了某种版本标记,请确保它们能够进入服务工作者脚本。
当然,这并不能改变CSS文件上的缓存头需要正确设置的事实。否则,服务工作者将只加载已经缓存在浏览器缓存中的文件。
一种选择是只使用服务工作者缓存中的缓存作为备用,并始终尝试通过fetch()
首先进入网络。但是,您会失去缓存优先策略提供的一些性能增益。
另一种方法是使用sw-preache
生成Service Worker脚本,作为站点构建过程的一部分。
它生成的服务工作者将使用文件内容的散列来检测更改,并在部署新版本时自动更新缓存。它还将使用缓存破坏URL查询参数,以确保您不会意外地使用HTTP缓存中的过期版本填充service worker缓存。
在实践中,您最终会遇到一个使用性能友好的缓存优先策略的服务工作人员,但是缓存将在页面加载后“在后台”更新,以便下次访问时一切都是新鲜的。如果需要,可以向用户显示一条消息,让他们知道有更新的内容可用,并提示他们重新加载。
情况: 在我的网站上。com/game,我在 在我的服务器上,的最大值为。 问题: 。 问题是,每当用户访问,它显示空页面,不再执行任何操作。我无法让客户端获取新的。 如何让客户端获取新的?
有一个问题,我们的一些用户有一个错误的服务工作者坐在他们的Facebook浏览器从我们的网站。 问题:Facebook App用户在访问我们在FB上共享的页面时,会在FB浏览器上看到我们的“您离线页面”。 错误似乎是旧版本的Google Workbox(3.6.1)使用Chrome75自动返回FB应用程序中的您离线页面。更新Workbox修复了它。 对Workbox的引用在service work
我创建了react应用程序,它使用股票服务人员缓存文件。我希望该应用程序是一个可下载的PWA,但每当我更新(与heroku) 我是否错过了文档中的一些配置,以便在其为PWA时启用自动更新?或者我可以做些什么来在更新时强制刷新。(我读过这是一个糟糕的UI,但我对此没有意见) 注意:从PC上的浏览器查看时,它会按预期工作。(选项卡全部关闭时更新)
我遵循angular的官方指南,成功安装了初始服务人员。接下来,我想尝试一下更新行为,得到了一个 无法读取未定义的属性id 其中as id指ngsw-worker.js方法中的client.id 我做的步骤是 注册SW,离线并重新加载(工作),关闭标签,但保持窗口打开 app.cmp.html更新代码 重建项目(ng build--prod) 打开新的Tab和应用程序(可以观察SW更新) 重新加载
根据MDN上的本文件: 之后,每隔24小时左右下载一次。它的下载频率可能会更高,但必须每24小时下载一次,以防止糟糕的脚本被打扰太久。 Firefox和Chrome也是如此吗?还是只有当用户导航到站点时才会更新服务人员javascript?
我使用这个众所周知的模式来显示服务工作者更新准备好安装时的通知(此代码进入网页,当然不是服务工作者代码): 当然,该代码是有效的,因为如果准备好安装新版本的服务辅助角色,它会在网页上显示通知。 问题是,如果在该点重新加载页面,则不再显示通知,因为如果安装了新的服务工作人员并等待激活,则不再触发事件。 因此,通知只出现一次,当安装了新的服务工作人员并等待激活并开始控制页面时,但是一旦页面重新加载,通