有很多面试题里面,有关于 service worker 的,比如说关于浏览器的缓存。
面试官问你,浏览器中的缓存位置有哪几种?你一搜,答案是:
浏览器中的缓存位置一共有四种:按优先级从高到第排列分别是:
- Service Worker
- MEmory Cache
- Disk Cache
- Push Cache
先不管其他的,我们先来看看 service worker 是个什么东西呢?请看官方文档
Service Worker API - Web API 接口参考 | MDN
Service worker 是一个注册在指定源和路径下的事件驱动 worker。它采用 JavaScript 文件的形式,控制关联的页面或者网站,拦截并修改访问和资源请求,细粒度地缓存资源。你可以完全控制应用在特定情形(最常见的情形是网络不可用)下的表现。
重点是,使用 service worker 可以拦截并修改访问的资源请求,进而实现缓存资源。
还有一个场景可以使用 service worker,假设我们有一个A网站 https://a.com,如果这个网站由于某种原因被有关部门给封了,我们可以使用 service worker 拦截 A 网站的请求,然后重写网页内容。
在网站首页的 js 脚本中注册
try {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js', {
scope: '/'
});
}
} catch (error) {
console.error(`注册失败:${error}`);
}
实现 sw.js 的代码
// 要展示给用户的新的页面代码
const newHtml = `
当前页面正在维护,
请前往 新网站
`;
// 下载 service worker
self.addEventListener('install', () => {
self.skipWaiting();
console.log('service worker install');
});
// 监听
self.addEventListener('controllerchange', () => {
console.log('service worker controllerchange');
if (refreshing) {
return;
}
refreshing = true;
window.location.reload();
});
// 监听所有的请求
self.addEventListener('fetch', async event => {
try {
// 拦截 document 类型
if (event.request.destination === 'document') {
const finalResult = async () => {
try {
const fetchResponse = await fetch(event.request);
const reader = fetchResponse.body.getReader();
// 使用可读流
const readerStream = new ReadableStream({
start(controller) {
function push() {
reader.read().then(({ done, value }) => {
if (done) {
controller.close();
return;
}
controller.enqueue(value);
push();
});
}
push();
}
});
// 新建响应数据
const responseText = await new Response(readerStream, {
headers: { 'Content-Type': 'text/html' }
}).text();
// 响应内容不是自己的网站,替换响应内容
// 我们的网站 有一个 id 为 a-website-html-wrapper 的元素
if (responseText.indexOf('a-website-html-wrapper') < 0) {
return new Response(newHtml, {
headers: {
'Content-Type': 'text/html; charset=UTF-8'
},
status: 200
});
}
return new Response(responseText, {
headers: {
'Content-Type': 'text/html; charset=UTF-8'
},
status: 200
});
} catch (err) {
// 离线也返回替代页面
return new Response(newHtml, {
headers: {
'Content-Type': 'text/html; charset=UTF-8'
},
status: 200
});
}
};
event.respondWith(finalResult());
}
} catch (error) {
console.log('service worker', error);
return new Response(responseText, {
headers: {
'Content-Type': 'text/html; charset=UTF-8'
},
status: 200
});
}
});