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/

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

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

相关推荐
蜗牛快跑2134 分钟前
面向对象编程 vs 函数式编程
前端·函数式编程·面向对象编程
Dread_lxy5 分钟前
vue 依赖注入(Provide、Inject )和混入(mixins)
前端·javascript·vue.js
涔溪1 小时前
Ecmascript(ES)标准
前端·elasticsearch·ecmascript
榴莲千丞1 小时前
第8章利用CSS制作导航菜单
前端·css
奔跑草-1 小时前
【前端】深入浅出 - TypeScript 的详细讲解
前端·javascript·react.js·typescript
羡与1 小时前
echarts-gl 3D柱状图配置
前端·javascript·echarts
guokanglun1 小时前
CSS样式实现3D效果
前端·css·3d
咔咔库奇1 小时前
ES6进阶知识一
前端·ecmascript·es6
前端郭德纲1 小时前
浏览器是加载ES6模块的?
javascript·算法
JerryXZR1 小时前
JavaScript核心编程 - 原型链 作用域 与 执行上下文
开发语言·javascript·原型模式