有没有办法可以加载一个初始脚本(页面顶部的文件),然后在页面继续加载时尝试拦截所有其他资源请求(脚本,css,图像等)?
此拦截的目的是以不依赖于应用程序/页面如何组合在一起的不可知方式从缓存(localStorage,indexedDB,other)或甚至通过webrtc从远程对等方提供文件.
我知道缓存清单/离线方法,但这里的重点是获取所请求的资源并从选择的位置代理它.
解决方案是注册一个服务工作者来拦截第一个脚本内的请求。
唯一的问题是这种注册是异步的,而资源是同步加载的,这意味着在第一次加载时将获取原始资源。
一旦服务工作者被加载,简单的页面刷新就可以加载拦截的资源。
下面的代码检测Service Worker是否是第一次(或之后Ctrl+F5)加载,如果是,则在注册回调中执行页面刷新。它是上述问题的解决方案,可能不是最优雅的。
在下面的示例中,image/js 资源是从外部 url 加载的,但它可以是任何源。script.js可以添加到所有页面的顶部。
https://next.plnkr.co/edit/dP6wr0hB1gbXDeQs?preview
html
<html>
<body>
<h2>My intercepted page</h2>
<script src="script.js"></script>
<script src="jquery.js"></script>
<div class="placeholder">a</div>
<img src="z9t29Bk.gif">
<img src="y2kChjZ.gif">
<script>$(".placeholder").text("loaded jquery version:"+$.fn.jquery)</script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)
脚本.js
if ('serviceWorker' in navigator) {
var interceptorLoaded = navigator.serviceWorker.controller!=null;
window.addEventListener('load', function() {
navigator.serviceWorker.register('sw.js')
.then(function(registration){
console.log('ServiceWorker registration successful with scope: ', registration.scope);
if(!interceptorLoaded){
//refresh after interceptor was loaded but only if the interceptor was not already loaded.
window.location=window.location.href;
}
},
function(err) { // registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
});
}
Run Code Online (Sandbox Code Playgroud)
sw.js
self.addEventListener('fetch', function(event) {
console.log("REQUEST:", event.request.url);
var url = event.request.url;
if (url.endsWith("/jquery.js")) {
event.respondWith(
fetch('https://code.jquery.com/jquery-3.3.1.js')
);
}else if(url.endsWith(".jpg") || url.endsWith(".png") || url.endsWith(".gif")){
event.respondWith(fetch("https://i.imgur.com/"+url.substring(url.lastIndexOf("/")+1),{
mode: 'cors',
}));
}
})
Run Code Online (Sandbox Code Playgroud)
参考:https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer