【发布时间】:2017-08-16 14:59:46
【问题描述】:
我正在开发一个具有所有功能(如离线、添加到主屏幕、通知等)的 PWA,但是当我尝试刷新 UI 时遇到了一些问题。
换句话说,我的 PWA 具有在 index.html 文件中定义的 UI,我想做的是缓存 PWA 以供离线使用(我可以这样做),如果设备在线检查是否有一些 UI 更新(在 index.html 文件中,或在它依赖的文件中),下载此更改并刷新设备上和缓存内的 UI。
有了数据,我对缓存没有任何问题。
例如如果我有这个页面index.html:
<!DOCTYPE html>
<html>
<head >
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title class="title">PWA</title>
<link rel="manifest" href="manifest.json">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="PWA">
<link rel="apple-touch-icon" href="imgs/icons/Icon-152.png">
<meta name="msapplication-TileImage" content="imgs/icons/Icon-144.png">
<meta name="msapplication-TileColor" content="#2F3BA2">
<link rel="shortcut icon" sizes="32x32" href="imgs/icons/Icon-32.png">
<link rel="shortcut icon" sizes="196x196" href="imgs/icons/Icon-196.png">
<link rel="apple-touch-icon-precomposed" href="imgs/icons/Icon-152.png">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-12">
<img src="imgs/images.png">
</div>
</div>
</div>
<script src="js/jquery.js"></script>
<script src="js/bootstrap.js"></script>
<script src="js/app.js"></script>
</body>
</html>
app.js:
if('serviceWorker' in navigator) {
navigator.serviceWorker
.register('service-worker.js')
.then(function() { console.log('Service Worker Registered'); });
}
service-worker.js:
var dataCacheName = 'dataCache-v2';
var cacheName = 'cache-v2';
var filesToCache = [
'index.html',
'imgs/images.png',
'src/app.js'
];
self.addEventListener('install', function(e) {
console.log('[ServiceWorker] Install');
e.waitUntil(
caches.open(cacheName).then(function(cache) {
console.log('[ServiceWorker] Caching App Shell');
return cache.addAll(filesToCache);
})
);
});
self.addEventListener('activate', function(e) {
console.log('[ServiceWorker] Activate');
e.waitUntil(
caches.keys().then(function(keyList) {
return Promise.all(keyList.map(function(key) {
console.log('[ServiceWorker] Removing old cache', key);
if (key !== cacheName && key !== dataCacheName) {
return caches.delete(key);
}
}));
})
);
return self.clients.claim();
});
self.addEventListener('fetch', function(e) {
console.log('[ServiceWorker] Fetch', e.request.url);
var dataUrl = 'URL-WHERE-FIND-DATA';
if (e.request.url.indexOf(dataUrl) === 0) {
e.respondWith(
fetch(e.request)
.then(function(response) {
return caches.open(dataCacheName).then(function(cache) {
cache.put(e.request.url, response.clone());
console.log('[ServiceWorker] Fetched&Cached Data');
return response;
});
})
);
} else {
e.respondWith(
caches.match(e.request).then(function(response) {
return response || fetch(e.request);
})
);
}
});
假设我将images.png 替换为另一个图像但名称相同,我如何向用户显示新图像?
如果我刷新页面,数据是从网络获取的(如果可用),但图像仍然从缓存中捕获。
我希望我已经很好地解释了我的问题。非常感谢所有帮助我的人
更新 #1:
我尝试按照 Arnelle Balane 对我的建议实施“先缓存后网络”策略,但问题始终存在,浏览器始终显示缓存的图像(在下面的代码中,我尝试更新名为“demo.xml”的图像) .jpg')。 可能我做错了什么。 这是我的代码:
service-worker.js:
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.open("my-cache").then(function(cache) {
return fetch(event.request).then(function(response) {
console.log('Fetch: ' + response.url);
cache.put(event.request, response.clone());
return response;
});
})
);
});
app.js:
var networkDataReceived = false;
var networkUpdate = fetch('https://website.com/app/images/demo.jpg').then(function(response) {
return response.blob();
}).then(function(data) {
networkDataReceived = true;
updatePage(data);
});
caches.match('https://website.com/app/images/demo.jpg').then(function(response) {
if (!response) throw Error("No data");
return response.blob();
}).then(function(data) {
if (!networkDataReceived) {
updatePage(data);
}
}).catch(function() {
return networkUpdate;
}).catch(showErrorMessage);
function showErrorMessage(response){
console.log("Error: " + response);
}
function updatePage(response) {
var img = document.getElementById('demo');
var imageUrl = URL.createObjectURL(response);
img.src = imageUrl;
}
有什么新建议吗?谢谢
更新 #2:
现在我正在尝试从头开始做所有事情。
我已经从这个谷歌的例子中复制了服务工作者:https://developers.google.com/web/fundamentals/getting-started/codelabs/your-first-pwapp/
这实现了“缓存然后网络”策略。
service-worker.js的代码是这样的:
var dataCacheName = 'dataCache1';
var cacheName = 'cache1';
var filesToCache = [
'/',
'index.html',
'css/main.css',
'src/app.js'
];
self.addEventListener('install', function(e) {
console.log('[ServiceWorker] Install');
e.waitUntil(
caches.open(cacheName).then(function(cache) {
console.log('[ServiceWorker] Caching app shell');
return cache.addAll(filesToCache);
})
);
});
self.addEventListener('activate', function(e) {
console.log('[ServiceWorker] Activate');
e.waitUntil(
caches.keys().then(function(keyList) {
return Promise.all(keyList.map(function(key) {
if (key !== cacheName && key !== dataCacheName) {
console.log('[ServiceWorker] Removing old cache', key);
return caches.delete(key);
}
}));
})
);
/*
* Fixes a corner case in which the app wasn't returning the latest data.
* You can reproduce the corner case by commenting out the line below and
* then doing the following steps: 1) load app for first time so that the
* initial New York City data is shown 2) press the refresh button on the
* app 3) go offline 4) reload the app. You expect to see the newer NYC
* data, but you actually see the initial data. This happens because the
* service worker is not yet activated. The code below essentially lets
* you activate the service worker faster.
*/
return self.clients.claim();
});
self.addEventListener('fetch', function(e) {
console.log('[Service Worker] Fetch', e.request.url);
var dataUrl = 'https:/mywebsite.it/service/images/demo.jpg';
if (e.request.url.indexOf(dataUrl) > -1) {
/*
* When the request URL contains dataUrl, the app is asking for fresh
* weather data. In this case, the service worker always goes to the
* network and then caches the response. This is called the "Cache then
* network" strategy:
* https://jakearchibald.com/2014/offline-cookbook/#cache-then-network
*/
e.respondWith(
caches.open(dataCacheName).then(function(cache) {
return fetch(e.request).then(function(response){
cache.put(e.request.url, response.clone());
return response;
});
})
);
} else {
/*
* The app is asking for app shell files. In this scenario the app uses the
* "Cache, falling back to the network" offline strategy:
* https://jakearchibald.com/2014/offline-cookbook/#cache-falling-back-to-network
*/
e.respondWith(
caches.match(e.request).then(function(response) {
return response || fetch(e.request);
})
);
}
});
结果总是一样的,图像不会更新。 使用 fetch(url) 方法,服务工作者应该从网络中获取图像,对吧? 代码结果如下图所示。
当我重新加载页面时,唯一获取的请求是文件夹“/service/”
如果我尝试通过对浏览器 (https://mywebsite.com/service/images/demo.jpg) 的显式请求来“强制”加载我想要更新的图像,服务工作者会正确获取请求,但始终显示旧图像。 我认为我在做一些愚蠢的错误,但我不明白是什么。
【问题讨论】:
-
有几种方法可以处理静态文件缓存。您可以在您的网络服务器(nginx)或 webpack(javascript 应用程序的模块捆绑器)中处理它
-
您认为哪种方法最好?现在我看到了 sw-precache。也许它可以做我想做的事,因为它对每个文件进行哈希处理以捕获新版本(如果存在)(对吗?)
-
我在我的项目中使用了 react js,它与 webpack 配合得很好。 Webpack 有一个称为“加载器”的概念,它为您预处理静态文件。您可以查看文档以更好地理解它。 webpack.js.org/loaders/html-loader
标签: javascript caching web-applications service-worker progressive-web-apps