相关文章:HTTP缓存浅析与应用
Service Worker 在浏览器中实际上是以后台进程的方式运行的。它在浏览器控制台关闭的情况下仍然可以继续运行,并且可以处理诸如推送通知、离线缓存、网络代理、缓存资源等任务。
使用场景&好处:
-
离线访问:Service Worker 可以缓存网站资源,使用户在离线状态下也能访问页面内容。
-
快速加载:通过缓存策略,Service Worker 可以加速网页加载速度,提升用户体验。
-
后台数据同步:Service Worker 可以在后台进行数据同步,保持应用数据的最新性。
-
推送通知:Service Worker 可以接收推送消息,实现网站的消息推送功能。
-
路由控制:Service Worker 可以拦截和处理网络请求,实现自定义路由和缓存策略。
-
降低带宽消耗:通过缓存静态资源,可以减少网络流量消耗,特别是对于移动设备用户来说,可以节省用户的流量费用和提升用户体验。
-
优化性能:缓存静态资源可以减少网络请求次数,减少等待时间,从而优化网站性能。这对于移动端设备或网络条件较差的用户尤为重要。
-
减少服务器负载:通过缓存静态资源,可以减轻服务器的负载压力,提高网站的并发访问能力。
如何使用:
- 注册 Service Worker
在网页中注册 Service Worker,通常在主线程代码中通过 JavaScript 来完成。例如:
// index.html
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/x-icon" href="./images/favicon.ico">
<title>Service Worker Example</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<h1 class="h1">Hello, Service Worker!</h1>
<img class="flour" src="./images/flour.jpg" alt="flour">
<script src="./main.js"></script>
<script>
// 注册 Service Worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('./service-worker.js')
.then(registration => {
console.log('Service Worker registered:', registration);
registration.update(); // 通知 Service Worker 进行更新
})
.catch(error => {
console.error('Service Worker registration failed:', error);
});
});
}
</script>
</body>
</html>
- 编写 Service Worker 脚本
编写 Service Worker 脚本(如上面注册的 service-worker.js 文件),实现所需的功能,例如缓存策略、拦截请求等。
- 监听事件
在 Service Worker 脚本中监听 install、activate 和 fetch 等事件,用于安装、激活和处理网络请求等操作。
- 缓存资源
在 Service Worker 中使用 Cache Storage API 缓存网站资源,以便实现离线访问和快速加载功能。
- 更新 Service Worker
当 Service Worker 脚本发生变化时,需要更新注册逻辑,以便让新的 Service Worker 生效。
文件目录
lua
|-- images
| |-- favicon.ico
| `-- flour.jpg
|-- index.html
|-- main.js
|-- service-worker.js
`-- style.css
完整的 service-worker.js
文件如下:
js
// service-worker.js
// Service Worker 缓存名称(版本号)
const CACHE_NAME = "my-site-cache-v2";
// 需要缓存的资源
const urlsToCache = [
"./style.css",
"./main.js",
"./images/flour.jpg",
"./images/favicon.ico",
];
// 设置缓存的最大存活时间(单位:秒)
const MAX_AGE_SECONDS = 24 * 60 * 60; // 24 hours
console.log("self", self);
// 安装 Service Worker
self.addEventListener("install", (event) => {
console.log("install--event", event);
// event.waitUntil 来确保 Service Worker 不会在异步操作完成前被终止。
// 确保在异步操作完成之前,Service Worker 会一直处于活动状态。
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
console.log("Cache opened");
return cache.addAll(urlsToCache);
})
);
});
/**
* 缓存策略:
* Cache First 策略
* 如果缓存存在且未过期,则返回缓存。若缓存不存在或已过期,则发起网络请求,并在获取到响应后将其加入缓存。
* */
// 拦截请求并返回缓存或网络请求
self.addEventListener("fetch", (event) => {
console.log("fetch--event", event.request.url);
// 检查请求的 URL 或其他属性,判断是否为特殊请求(如来自 Chrome 扩展程序的请求)
if (event.request.url.startsWith("chrome-extension://")) {
// 如果是特殊请求,可以选择直接返回,不继续处理
return;
}
// 对于非特殊请求,继续执行缓存策略
// event.respondWith() 拦截网络请求并提供自定义响应
event.respondWith(
caches.match(event.request).then((cachedResponse) => {
if (cachedResponse) {
const ageInSeconds =
(Date.now() -
new Date(cachedResponse.headers.get("date")).getTime()) /
1000;
console.log("ageInSeconds", ageInSeconds, MAX_AGE_SECONDS);
if (ageInSeconds < MAX_AGE_SECONDS) {
return cachedResponse;
}
}
// 超过缓存时间后,重新请求资源,并更新缓存
return fetch(event.request).then((response) => {
console.log("puting", response.url);
if (response.status === 200) {
const responseToCache = response.clone();
caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, responseToCache);
});
}
return response;
});
})
);
});
// 更新事件 (更改 Service Worker 缓存版本号 触发)
// 当新的 Service Worker 激活时,清理旧缓存
self.addEventListener("activate", (event) => {
console.log("activate--event", event);
event.waitUntil(
caches.keys().then((cacheNames) => {
console.log("cacheNames", cacheNames);
return Promise.all(
// cacheNames.map((cacheName) => {
// // 删除旧版本缓存
// if (cacheName !== CACHE_NAME) {
// return caches.delete(cacheName);
// }
// })
cacheNames
.filter((cacheName) => {
// 过滤出需要更新的缓存名称
return (
cacheName.startsWith("my-site-cache-") && cacheName !== CACHE_NAME
);
})
.map((cacheName) => {
return caches.delete(cacheName); // 删除旧版本的缓存
})
);
})
);
});
style.css
文件
css
.h1 {
color: red;
font-size: 32px;
font-weight: bold;
}
.flour {
display: inline-block;
width: 700px;
height: 500px;
}
main.js
文件
js
function test() {
console.log("exec main.js test()");
}
test();
启动查看
Service Worker 必须通过 HTTPS 协议提供服务,或者在 localhost 环境下开发时可以使用 HTTP 协议。
所以需要在一个本地的开发服务器上运行你的网站,比如使用 Node.js 的 http-server 模块
-
首先确保你已经安装了 Node.js。
-
使用 npm 安装 http-server 模块:
npm install -g http-server
(可能需要管理员权限或者在 macOS 和 Linux 上使用 sudo)
- 在命令行或终端中进入到你的网站根目录,然后运行命令
http-server
http-server -c10
设置静态文件强制缓存时间为10s,不指定参数,则默认3600 seconds
- 然后在浏览器中输入 http://localhost:8080(默认端口是 8080)即可访问你的网站。
实现效果
- 查看Service Worker 后台进程
在浏览器界面,按快捷键: Shift+Esc
- 离线缓存
F12 打开控制台,在network里面修改网络为offline状态,刷新并查看页面
扩展
可以在 Service Worker 中使用不同的缓存策略来控制缓存的行为。以下是一些常见的缓存策略及其实现方式:
- Cache First 策略:首先检查缓存中是否有匹配的响应,如果有则直接返回缓存内容,否则向网络请求数据并将响应存入缓存。
js
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request).then(function(response) {
return response || fetch(event.request).then(function(networkResponse) {
return caches.open('my-cache').then(function(cache) {
cache.put(event.request, networkResponse.clone());
return networkResponse;
});
});
})
);
});
- Network First 策略:优先从网络获取数据,如果网络请求失败或超时,则返回缓存中的响应。
js
self.addEventListener('fetch', function(event) {
event.respondWith(
fetch(event.request).catch(function() {
return caches.match(event.request);
})
);
});
- Stale-While-Revalidate 策略:返回缓存响应同时发起网络请求,并在后台更新缓存。下次再请求相同资源时,会返回更新后的缓存响应。
js
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.open('my-cache').then(function(cache) {
return cache.match(event.request).then(function(response) {
var fetchPromise = fetch(event.request).then(function(networkResponse) {
cache.put(event.request, networkResponse.clone());
return networkResponse;
});
return response || fetchPromise;
});
})
);
});
注意
-
清空缓存并硬性重新加载会把缓存清除掉
-
Service Worker 脚本必须使用正确的 MIME 类型(例如 'application/javascript'),而不是 'text/plain'。如果使用 Node.js 的 http-server 启动的本地服务,一般情况下会正确返回 JavaScript 文件的 MIME 类型,但如果有特殊配置可能需要手动设置。
-
静态资源有更新,则可以修改
CACHE_NAME
缓存版本号。
刚学习的,请大神多指点!
我是 甜点cc ,个人网站: blog.i-xiao.space/
己所不欲勿施于人,己所欲亦勿施于人!
公众号:【看见另一种可能】