我目前正在试验的服务工作人员代码部分如下所示
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open('v1').then(function(cache) {
return cache.addAll([
'/react-redux/node_modules/react/dist/react-with-addons.js',
'/react-redux/node_modules/react-dom/dist/react-dom.js',
'/react-redux/a.js'
]);
})
);
});当然,使用从缓存返回的标准fetch事件侦听器,或者在项不存在的情况下运行网络请求。
但是,如果从上面的示例中可以看出,a.js,而只有a.js被更新了--我如何让服务工作人员更新该文件,而只更新该文件;以及如何确保用户下次浏览到我的页面时,他们不会从服务工作人员中提取现在失效的文件版本?
我能想象的最好的方法就是在这些文件urls中添加一个缓存破坏程序,例如
'/react-redux/node_modules/react/dist/react-with-addons.js?hash=1MWRF3...'
然后用相同的当前散列/缓存破坏程序更新我用来请求这些文件的模块加载器,然后在SW install事件中迭代当前缓存键并删除任何过时的内容,并添加任何丢失的内容。
这似乎解决了这两个问题:当一个文件被更新时,发送的网络请求将与现在失效的服务工作人员中的任何内容不匹配,因此也会发生相同的网络回退;并且在Service的安装事件中的选择性缓存插入不会尝试向缓存中添加已经存在和当前的内容。
当然,服务工作人员代码会随着这些哈希值的变化而改变(自动从构建过程中),因此,当文件发生更改时,也会让SW重新安装。
但我情不自禁地认为还有更简单的方法。在那里吗?
发布于 2017-01-12 16:09:13
您对理想情况的理解,以及在确保高速缓存的资产被高效可靠地更新方面的困难,都是当真的。
虽然您可以使用自己的方法,但现有的工具将自动对每个文件进行指纹识别,然后生成一个管理缓存资产的服务工作人员文件。我开发了其中一个,sw-precache。offline-plugin是另一个覆盖类似领域的选择。
发布于 2018-05-17 16:10:19
最后,我按照您所说的编写了代码,这是为那些自己编写代码有困难的人编写的代码:
首先,每次包发生变化时,我们都需要编写代码将时间戳/散列添加到包文件的URL中。
我们中的大多数人都使用webpack将应用程序捆绑在一起,每次webpack配置文件被执行时,包应该会发生变化,因此我们将在URL中进行散列/时间戳插入。我有一个名为index.template.html的文件,在这里我存储了提供给用户的文件,所以为了修改URL,我做了以下工作:
// webpack.config.js
const webpack = require('webpack');
const fs = require('fs');
fs.readFile('./public/index.template.html', function (err, data) {
if (err) return console.log('Unable to read index.template file', err);
fs.writeFile('./public/index.template.html',
// finding and inserting current timestamp in front of the URL for cache busting
data.toString('utf8').replace(/bundle\.js.*"/g, "bundle\.js\?v=" + Math.floor(Date.now() / 1000) + "\""),
(err) => {
if (err) console.log("Unable to write to index.template.html", err);
});
});
module.exports = {
// configuration for webpack
};下面是服务工作者的代码,该代码检测URL和重取中的更改,并在发生更改时替换缓存中的资源,我试图解释在注释中的工作:
self.addEventListener("fetch", function (event) {
event.respondWith(
// intercepting response for bundle.js since bundle.js may change and we need to replace it in our cahce
event.request.url.indexOf('public/bundle.js') != -1 ?
checkBundle(event.request) : //if it is the bundle URL then use our custom function for handling the request
caches.match(event.request) //if its not then do the use service-worker code:
.then(function(response) {
// other requests code
})
);
});
// our custom function which does the magic:
function checkBundle(request) {
return new Promise(function(resolve, reject){ // respondWith method expects a Promise
caches.open(cacheName).then(function(cache) {
//first lets check whether its in cache already or not
// ignoreSearch parameter will ignore the query parameter while searching in cache, i.e., our cache busting timestmap
cache.keys(request, { ignoreSearch: true }).then(function(keys) {
if(keys.length == 0) {
// its not in cache so fetch it
return resolve(fetch(request).then(
function (response) {
if (!response || (response.status !== 200 && response.status !== 0)) {
return response;
}
cache.put(request, response.clone());
return response;
}
));
}
//it is in cache, so now we extract timestamp from current and cached URL and compare them
const lastVersion = /bundle.js\?v=(.*)$/.exec(keys[0].url)[1],
curVersion = /bundle.js\?v=(.*)$/.exec(request.url)[1];
if(lastVersion == curVersion) // if timestamp is change that means no change in the resource
return resolve(cache.match(request)); //return the cached resource
//bundle file has changed, lets delete it from cache first
cache.delete(keys[0]);
//now we fetch new bundle and serve it and store in cache
var fetchRequest = request.clone();
resolve(fetch(fetchRequest).then(
function (response) {
if (!response || (response.status !== 200 && response.status !== 0)) {
return response;
}
cache.put(request, response.clone());
return response;
}
));
});
});
});
}As mentioned by Jeff Posnick in the comment of other answers通常这些类型的方法需要N+1访问来查看更新的资源,但是这个方法不需要重新获取资源,然后提供给客户端,同时在缓存中进行替换。
https://stackoverflow.com/questions/41616947
复制相似问题