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

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 生效。

文件目录

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 模块

  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)即可访问你的网站。

实现效果

  1. 查看Service Worker 后台进程

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

  1. 离线缓存

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

扩展

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

  1. 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;
                });
            });
        })
    );
});
  1. Network First 策略:优先从网络获取数据,如果网络请求失败或超时,则返回缓存中的响应。
js 复制代码
self.addEventListener('fetch', function(event) {
    event.respondWith(
        fetch(event.request).catch(function() {
            return caches.match(event.request);
        })
    );
});
  1. 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;
            });
        })
    );
});

注意

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

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

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


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

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

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

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

相关推荐
腾讯TNTWeb前端团队3 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰7 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪7 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪7 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy8 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom8 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom8 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom8 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom8 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom9 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试