Service Worker&Caches离线缓存

相关文章:HTTP缓存浅析与应用

Service Worker 在浏览器中实际上是以后台进程的方式运行的。它在浏览器控制台关闭的情况下仍然可以继续运行,并且可以处理诸如推送通知、离线缓存、网络代理、缓存资源等任务。

使用场景&好处:

  1. 离线访问:Service Worker 可以缓存网站资源,使用户在离线状态下也能访问页面内容。

  2. 快速加载:通过缓存策略,Service Worker 可以加速网页加载速度,提升用户体验。

  3. 后台数据同步:Service Worker 可以在后台进行数据同步,保持应用数据的最新性。

  4. 推送通知:Service Worker 可以接收推送消息,实现网站的消息推送功能。

  5. 路由控制:Service Worker 可以拦截和处理网络请求,实现自定义路由和缓存策略。

  6. 降低带宽消耗:通过缓存静态资源,可以减少网络流量消耗,特别是对于移动设备用户来说,可以节省用户的流量费用和提升用户体验。

  7. 优化性能:缓存静态资源可以减少网络请求次数,减少等待时间,从而优化网站性能。这对于移动端设备或网络条件较差的用户尤为重要。

  8. 减少服务器负载:通过缓存静态资源,可以减轻服务器的负载压力,提高网站的并发访问能力。

如何使用:

  1. 注册 Service Worker

在网页中注册 Service Worker,通常在主线程代码中通过 JavaScript 来完成。例如:

// index.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>
  1. 编写 Service Worker 脚本

编写 Service Worker 脚本(如上面注册的 service-worker.js 文件),实现所需的功能,例如缓存策略、拦截请求等。

  1. 监听事件

在 Service Worker 脚本中监听 install、activate 和 fetch 等事件,用于安装、激活和处理网络请求等操作。

  1. 缓存资源

在 Service Worker 中使用 Cache Storage API 缓存网站资源,以便实现离线访问和快速加载功能。

  1. 更新 Service Worker

当 Service Worker 脚本发生变化时,需要更新注册逻辑,以便让新的 Service Worker 生效。

文件目录

|-- images
|   |-- favicon.ico
|   `-- flour.jpg
|-- index.html
|-- main.js
|-- service-worker.js
`-- style.css

完整的 service-worker.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文件

.h1 {
color: red;
font-size: 32px;
font-weight: bold;
}
.flour {
display: inline-block;
width: 700px;
height: 500px;
}

main.js文件

function test() {
console.log("exec main.js test()");
}
test();

启动查看

Service Worker 必须通过 HTTPS 协议提供服务,或者在 localhost 环境下开发时可以使用 HTTP 协议。

所以需要在一个本地的开发服务器上运行你的网站,比如使用 Node.js 的 http-server 模块

  1. 首先确保你已经安装了 Node.js。

  2. 使用 npm 安装 http-server 模块:

npm install -g http-server
(可能需要管理员权限或者在 macOS 和 Linux 上使用 sudo)

  1. 在命令行或终端中进入到你的网站根目录,然后运行命令 http-server

http-server -c10 设置静态文件强制缓存时间为10s,不指定参数,则默认3600 seconds

  1. 然后在浏览器中输入 http://localhost:8080(默认端口是 8080)即可访问你的网站。

Service Worker&Caches离线缓存

实现效果

  1. 查看Service Worker 后台进程

在浏览器界面,按快捷键: Shift+Esc

Service Worker&Caches离线缓存

  1. 离线缓存

Service Worker&Caches离线缓存

F12 打开控制台,在network里面修改网络为offline状态,刷新并查看页面

Service Worker&Caches离线缓存

扩展

可以在 Service Worker 中使用不同的缓存策略来控制缓存的行为。以下是一些常见的缓存策略及其实现方式:

  1. Cache First 策略:首先检查缓存中是否有匹配的响应,如果有则直接返回缓存内容,否则向网络请求数据并将响应存入缓存。
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;
});
});
})
);
});
  1. Network First 策略:优先从网络获取数据,如果网络请求失败或超时,则返回缓存中的响应。
self.addEventListener('fetch', function(event) {
event.respondWith(
fetch(event.request).catch(function() {
return caches.match(event.request);
})
);
});
  1. Stale-While-Revalidate 策略:返回缓存响应同时发起网络请求,并在后台更新缓存。下次再请求相同资源时,会返回更新后的缓存响应。
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;
});
})
);
});

注意

  1. 清空缓存并硬性重新加载会把缓存清除掉

  2. Service Worker 脚本必须使用正确的 MIME 类型(例如 ‘application/javascript’),而不是 ‘text/plain’。如果使用 Node.js 的 http-server 启动的本地服务,一般情况下会正确返回 JavaScript 文件的 MIME 类型,但如果有特殊配置可能需要手动设置。

  3. 静态资源有更新,则可以修改CACHE_NAME缓存版本号。


刚学习的,请大神多指点!

我是 甜点cc,个人网站: blog.i-xiao.space/

己所不欲勿施于人,己所欲亦勿施于人!

公众号:【看见另一种可能】

原文链接:https://juejin.cn/post/7350541180616622132 作者:甜点cc

(0)
上一篇 2024年3月27日 上午10:23
下一篇 2024年3月27日 上午10:33

相关推荐

发表回复

登录后才能评论