如何使用 Service Worker 缓存外部 URL?
How can I cache external URLs using service worker?
我一直在玩 Google Web Starter Kit(https://github.com/google/web-starter-kit) and have got a little progressive web app working but am stuck on one thing: caching static files from external CDNs. E.g. I'm using MDL icons from https://fonts.googleapis.com/icon?family=Material+Icons 我看不到缓存请求的方法,因为 service worker 只响应 URLs在我的应用域内。
我看到的选项:
1. 下载文件并将其放在供应商文件夹中。优点:易于设置SW缓存。缺点:文件不会随着新图标的添加而保持最新(尽管这并不重要,因为我的代码只会使用可用的图标)。
使用 NPM 存储库:https://www.npmjs.com/package/material-design-icons 并使用构建步骤从 node_modules 复制 CSS 文件。优点:将允许从 NPM 自动更新。缺点:设置稍微复杂。
一些奇特的代理方法可以让我使用 SW 缓存外部 URL。例如myapp.com/loadExternal?url=https://fonts.googleapis.com/icon?family=Material+Icons
我现在倾向于 2,但很高兴知道 3 是否可行。
I can't see a way to cache the request as the service worker only
responds to URLs within my app domain.
这是不正确的。主动控制页面的 service worker 将有机会拦截和响应对跨域资源的网络请求;标准 fetch
事件将触发,并且 event.request.mode
将是 "cors"
或 "no-cors"
,具体取决于页面发出的请求的上下文。
简而言之,只要有一个 service worker 控制着一个页面,当该页面发出任何网络请求时,对于同源或跨源资源,service worker 将能够响应 fetch
事件。
我读了 sw-toolbox docs 并想出了如何去做。只需将其添加到我的运行时缓存中:
// cache fonts hosted on google CDN
global.toolbox.router.get(/googleapis/, global.toolbox.fastest);
TLDR:尝试选项 3。以后你会感谢我的。
来自 Google Docs:
默认情况下,如果不支持 CORS,从第三方 URL 获取资源将失败。您可以向 Request 添加一个 no-CORS
选项来解决这个问题,尽管这会导致 'opaque' 响应,这意味着您将无法判断响应是否成功。
所以
选项 1
添加no-cors
header
var CACHE_NAME = 'my-site-cache-v1';
var urlsToPrefetch = [
'/',
'/styles/main.css',
'/script/main.js',
'https://fonts.googleapis.com/icon?family=Material+Icons'
];
self.addEventListener('install', function(event) {
// Perform install steps
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
console.log('Opened cache');
// Magic is here. Look the mode: 'no-cors' part.
cache.addAll(urlsToPrefetch.map(function(urlToPrefetch) {
return new Request(urlToPrefetch, { mode: 'no-cors' });
})).then(function() {
console.log('All resources have been fetched and cached.');
});
})
);
});
正如OP所说,当资源更新时,在这种情况下很难获得最新的副本。另一个问题是,正如我所说,您不会知道响应是否成功。
选项 2
或者像 OP 所说的那样,我们可以创建一个代理服务器:一些简单的东西(伪代码,未测试,Node Express 代码)
var request = require('request');
app.get('/library', function(req,res) {
// read the param
var thirdPartyUrl = req.query.thirdPartyUrl;
request(thirdPartyUrl).pipe(res);
});
当您转到 /library?thirdPartyUrl=https://fonts.googleapis.com/icon?family=Material+Icons
时,应该给您响应并缓存它,就像我们通常缓存我们的响应一样。例如:删除 no-cors
并将 urlsToPrefetch
替换为以下值:
var urlsToPrefetch = [
'/',
'/library?thirdPartyUrl=https://fonts.googleapis.com/icon?family=Material+Icons',
'/library?thirdPartyUrl=https://fonts.googleapis.com/icon?family=Roboto'
];
选项 3
我认为这是最好、最简单的方法。使用工作箱。我们尝试过使用和不使用 workbox 创建 PWA,并且使用 workbox 很简单。
了解工作箱:https://developers.google.com/web/tools/workbox/
在初始设置后实现这样的路由:
workbox.routing.registerRoute(
new RegExp('^https://third-party.example.com/images/'),
new workbox.strategies.CacheFirst({
cacheName: 'image-cache',
plugins: [
new workbox.cacheableResponse.Plugin({
statuses: [0, 200],
})
]
})
);
我说的可能有点偏,但是是不是就这么简单呢?
caches.open(version)
.then(function(cache) {
return cache.addAll([
'/',
'index.html',
'/css/app.css',
'/js/app.min.js',
'/assets/images/logo_target_white.png',
'/scripts/angular-jwt.min.js',
'/scripts/ng-file-upload.min.js',
// this guy here
'https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css'
]);
})
使用此方法并在 chrome 工具中检查我的应用程序缓存似乎显示它缓存正确。
在服务中实现缓存时遇到同样的问题worker.The问题是如何从服务器实际获取图标。
1.Request 是通过主要 url(来自应用程序)(以黄色突出显示)制作的。
我想这是一个你试图缓存的静态请求。
2.Actual 获取图标的动态网络请求(以红色突出显示)。
要解决这个问题,您需要动态填充缓存(类似这样的东西)-
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then((response)=>{
if(response){
return response;
}
else{
return fetch(event.request) // response of requests
.then((res)=>{
return caches.open('dynamic') //create dynamic cache
.then((cache)=>{
cache.put(event.request.url,res.clone());
return res;
})
})
}
})
.catch(()=>{})
)
});
我一直在玩 Google Web Starter Kit(https://github.com/google/web-starter-kit) and have got a little progressive web app working but am stuck on one thing: caching static files from external CDNs. E.g. I'm using MDL icons from https://fonts.googleapis.com/icon?family=Material+Icons 我看不到缓存请求的方法,因为 service worker 只响应 URLs在我的应用域内。
我看到的选项: 1. 下载文件并将其放在供应商文件夹中。优点:易于设置SW缓存。缺点:文件不会随着新图标的添加而保持最新(尽管这并不重要,因为我的代码只会使用可用的图标)。
使用 NPM 存储库:https://www.npmjs.com/package/material-design-icons 并使用构建步骤从 node_modules 复制 CSS 文件。优点:将允许从 NPM 自动更新。缺点:设置稍微复杂。
一些奇特的代理方法可以让我使用 SW 缓存外部 URL。例如myapp.com/loadExternal?url=https://fonts.googleapis.com/icon?family=Material+Icons
我现在倾向于 2,但很高兴知道 3 是否可行。
I can't see a way to cache the request as the service worker only responds to URLs within my app domain.
这是不正确的。主动控制页面的 service worker 将有机会拦截和响应对跨域资源的网络请求;标准 fetch
事件将触发,并且 event.request.mode
将是 "cors"
或 "no-cors"
,具体取决于页面发出的请求的上下文。
简而言之,只要有一个 service worker 控制着一个页面,当该页面发出任何网络请求时,对于同源或跨源资源,service worker 将能够响应 fetch
事件。
我读了 sw-toolbox docs 并想出了如何去做。只需将其添加到我的运行时缓存中:
// cache fonts hosted on google CDN
global.toolbox.router.get(/googleapis/, global.toolbox.fastest);
TLDR:尝试选项 3。以后你会感谢我的。
来自 Google Docs:
默认情况下,如果不支持 CORS,从第三方 URL 获取资源将失败。您可以向 Request 添加一个 no-CORS
选项来解决这个问题,尽管这会导致 'opaque' 响应,这意味着您将无法判断响应是否成功。
所以
选项 1
添加no-cors
header
var CACHE_NAME = 'my-site-cache-v1';
var urlsToPrefetch = [
'/',
'/styles/main.css',
'/script/main.js',
'https://fonts.googleapis.com/icon?family=Material+Icons'
];
self.addEventListener('install', function(event) {
// Perform install steps
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
console.log('Opened cache');
// Magic is here. Look the mode: 'no-cors' part.
cache.addAll(urlsToPrefetch.map(function(urlToPrefetch) {
return new Request(urlToPrefetch, { mode: 'no-cors' });
})).then(function() {
console.log('All resources have been fetched and cached.');
});
})
);
});
正如OP所说,当资源更新时,在这种情况下很难获得最新的副本。另一个问题是,正如我所说,您不会知道响应是否成功。
选项 2
或者像 OP 所说的那样,我们可以创建一个代理服务器:一些简单的东西(伪代码,未测试,Node Express 代码)
var request = require('request');
app.get('/library', function(req,res) {
// read the param
var thirdPartyUrl = req.query.thirdPartyUrl;
request(thirdPartyUrl).pipe(res);
});
当您转到 /library?thirdPartyUrl=https://fonts.googleapis.com/icon?family=Material+Icons
时,应该给您响应并缓存它,就像我们通常缓存我们的响应一样。例如:删除 no-cors
并将 urlsToPrefetch
替换为以下值:
var urlsToPrefetch = [
'/',
'/library?thirdPartyUrl=https://fonts.googleapis.com/icon?family=Material+Icons',
'/library?thirdPartyUrl=https://fonts.googleapis.com/icon?family=Roboto'
];
选项 3
我认为这是最好、最简单的方法。使用工作箱。我们尝试过使用和不使用 workbox 创建 PWA,并且使用 workbox 很简单。
了解工作箱:https://developers.google.com/web/tools/workbox/
在初始设置后实现这样的路由:
workbox.routing.registerRoute(
new RegExp('^https://third-party.example.com/images/'),
new workbox.strategies.CacheFirst({
cacheName: 'image-cache',
plugins: [
new workbox.cacheableResponse.Plugin({
statuses: [0, 200],
})
]
})
);
我说的可能有点偏,但是是不是就这么简单呢?
caches.open(version)
.then(function(cache) {
return cache.addAll([
'/',
'index.html',
'/css/app.css',
'/js/app.min.js',
'/assets/images/logo_target_white.png',
'/scripts/angular-jwt.min.js',
'/scripts/ng-file-upload.min.js',
// this guy here
'https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css'
]);
})
使用此方法并在 chrome 工具中检查我的应用程序缓存似乎显示它缓存正确。
在服务中实现缓存时遇到同样的问题worker.The问题是如何从服务器实际获取图标。
1.Request 是通过主要 url(来自应用程序)(以黄色突出显示)制作的。 我想这是一个你试图缓存的静态请求。
2.Actual 获取图标的动态网络请求(以红色突出显示)。
要解决这个问题,您需要动态填充缓存(类似这样的东西)-
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then((response)=>{
if(response){
return response;
}
else{
return fetch(event.request) // response of requests
.then((res)=>{
return caches.open('dynamic') //create dynamic cache
.then((cache)=>{
cache.put(event.request.url,res.clone());
return res;
})
})
}
})
.catch(()=>{})
)
});