如何使用Service Worker?一文教你入门

Service Worker 是一种在后台中运行的脚本,能够拦截和处理网络请求,并且可以实现离线功能和推送通知。

简单来说,Service Worker 可以让我们写的 Web 应用更像一个原生应用表现。

一、概述

Service Worker 的诞生伴随着 PWA 技术,是现代 Web 技术的重要组成部分。

PWA (让浏览器弹出将网站安装应用程序)组成分为三部分:

  1. Service Worker
  2. HTTPS 协议(本地开发时可以使用 HTTP)
  3. manifest.json(Web 应用程序的清单文件)
如何使用Service Worker?一文教你入门

编写 PWA 应用,Service Worker 已经有越来越多的开发者所使用。

如何使用Service Worker?一文教你入门

图来自 State of JavaScript 2023:https://share.stateofjs.com/share/prerendered?localeId=zh-Hans&surveyId=state_of_js&editionId=js2023&blockId=browser_api_features&params=&sectionId=features

举两个可以完全离线使用的 Web 应用:

还有其他知名的 Web 应用使用了 Service Worker:

更多 PWA 应用可以在 https://www.pwalist.app/ 寻找试试

二、主要功能以及架构图

  • 离线支持:缓存资源,使应用在离线时也能运行。
  • 性能优化:通过缓存静态资源,减少网络请求,提高加载速度。
  • 后台同步:在网络连接恢复时同步数据。
  • 推送通知:即使应用未打开,也能接收通知。
如何使用Service Worker?一文教你入门

浏览器请求某一个资源时,会先调用 Service Worker,然后决定是发送一个新请求,还是从 Cache 当中存取。

三、生命周期

如何使用Service Worker?一文教你入门
  1. Installing 阶段:sw 安装时,此时刚开始注册
  2. Installed 阶段:sw 设置完成,等待其他 sw(过期)完成关闭
  3. Activating 阶段:确保新的 sw 能顺利接管控制权,主要用来清理旧缓存或其他sw的资源
  4. Activated 阶段:activated 阶段完成,sw 可以处理 Functional events(见下)
  5. Redundant 阶段:当一个sw 被另外一个替代时(此时当前 sw 就是过期)

四、事件

如何使用Service Worker?一文教你入门
  • Install 事件:安装事件在 Service Worker 被安装时触发。通常用于缓存静态资源,以便在离线时使用。
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open('my-cache').then((cache) => {
      return cache.addAll([
        '/',
        '/index.html',
        '/styles/main.css',
        '/scripts/main.js',
      ]);
    })
  );
});
  • Activate 事件:事件在 Service Worker 被激活时触发。用于清理旧缓存并确保新的 Service Worker 接管控制权。
self.addEventListener('activate', (event) => {
  const cacheWhitelist = ['my-cache'];
  event.waitUntil(
    caches.keys().then((cacheNames) => {
      return Promise.all(
        cacheNames.map((cacheName) => {
          if (!cacheWhitelist.includes(cacheName)) {
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
  self.clients.claim();
});
  • Message 事件(Activated 阶段):消息事件在 Service Worker 接收到消息时触发。可以用于与主线程进行通信。
// 主线程
navigator.serviceWorker.ready.then((registration) => {
  const messageChannel = new MessageChannel();
  messageChannel.port1.onmessage = (event) => {
    console.log("Received reply from Service Worker:", event.data);
  };

  registration.active.postMessage(
    { type: "MESSAGE_IDENTIFIER", payload: "Hello from main thread!" },
    [messageChannel.port2]
  );
});
self.addEventListener('message', (event) => {
  console.log('Received message:', event.data);
  if (event.data && event.data.type === 'MESSAGE_IDENTIFIER') {
    // 处理消息
    console.log('Payload:', event.data.payload);
  }
});
  • Fetch 事件:抓取事件在 Service Worker 拦截网络请求时触发。用于提供缓存响应或自定义网络请求处理。
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((response) => {
      return response || fetch(event.request).then((response) => {
        return caches.open('my-cache').then((cache) => {
          cache.put(event.request, response.clone());
          return response;
        });
      });
    })
  );
});
  • Sync 事件:同步事件在网络连接恢复时触发。用于后台同步数据。
// 主程
navigator.serviceWorker.ready.then((registration) => {
  return registration.sync.register('sync-tag');
});
self.addEventListener('sync', (event) => {
  if (event.tag === 'sync-tag') {
    event.waitUntil(
      // 执行同步任务,例如发送未发送的请求
      fetch('/sync-endpoint', {
        method: 'POST',
        body: JSON.stringify({ data: 'your data' }),
        headers: {
          'Content-Type': 'application/json'
        }
    );
  }
});
  • Push 事件:推送事件在接收到推送通知时触发。用于显示通知。(由服务器端推送服务产生事件派发)
self.addEventListener('push', (event) => {
  const data = event.data.json();
  self.registration.showNotification(data.title, {
    body: data.body,
    icon: '/images/icon.png',
  });
});

五、如何使用?

与 Web Worker 类似,来自一个域的多个页面共享一个 Service Worker。

1、第一步需要对当前的 Web 页面进行注入 Service Worker 代码,通过 navigator.serviceWorker.register 实现注册(进入 Installing 阶段)。

if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js')
      .then((registration) => {
        console.log('Service Worker registered: ', registration);
      })
      .catch((registrationError) => {
        console.log('Service Worker registration failed: ', registrationError);
      });
  });
}

如果是 React 项目,可以在根组件首次加载时注册

export default function App() {
    ...
    useEffect(() => {
        navigator.serviceWorker.register('/sw.js')
          .then((registration) => {
            console.log('Service Worker 注册成功: ', registration);
          })
          .catch((registrationError) => {
            console.log('Service Worker 注册失败: ', registrationError);
          });
    },[])
    ...
}

2、编写 Service Worker 代码(上面的 sw.js 文件内容)。第一次编写主要是缓存静态文件、清除旧缓存代码。

// sw.js

// Installing 阶段时调用
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open('my-cache').then((cache) => {
       // 需要缓存的路径、资源文件
      return cache.addAll([
        '/',
        '/index.html',
        '/styles/main.css',
        '/scripts/main.js',
      ]);
    })
  );
  self.skipWaiting();
});
// Activating 阶段时调用
self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((cacheNames) => {
      return Promise.all(
        cacheNames.map((cacheName) => {
          // 不是所要的缓存,删除
          if (cacheName !== 'my-cache') {
            return caches.delete(cacheName);
          }
        })
      );
    })
  ); 
  // 当前激活的 Service Worker 立即接管所有客户端,而不必等待页面重新加载
  self.clients.claim();
});

3、拦截和处理网络请求,当客户端(Web)发送网络请求时,此时让浏览器选择走 Cache 还是发送真请求

// sw.js

// Activated 阶段时调用
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((response) => {
      // cache 有,直接返回
      if (response) {
        return response;
      }
      // cache 没有,发送一次新请求
      return fetch(event.request).then((response) => {
        if (!response || response.status !== 200 || response.type !== 'basic') {
          return response;
        }
        // 复制到 cache,下次从这里取
        const responseToCache = response.clone();
        caches.open('my-cache').then((cache) => {
          cache.put(event.request, responseToCache);
        });
        return response;
      });
    })
  );
});

4、注意事项

  • HTTPS:Service Worker 只能在 HTTPS 或 localhost 上运行。
  • 浏览器支持:确保浏览器支持 Service Worker,可以参考 Can I Use
  • 缓存管理:定期更新缓存,处理缓存失效的情况。
  • PWA:2023年下半年开始,部分浏览器弹出安装应用程序提示框,不需要 Service Worker

通过以上步骤,你可以快速上手Service Worker,通过掌握这一技术,将让你的Web应用更加贴近原生应用体验,提升用户体验与应用的竞争力。

延展阅读:

如何搭建PostgreSQL高可用方案repmgr?详解安装部署与关键组件疑问

如何快速上手LoadRunner VuGen进行Web项目性能测试?

MongoDB Change Streams可以应用在哪些场景?它有哪些局限性?

如何有效通过电商全链路压测优化大型营销活动的性能?

MongoDB 4.0至7.0:这些主版本更新带来了哪些关键特性与性能飞跃?

咨询方案 获取更多方案详情                        
(3)
研发专家-天华研发专家-天华
上一篇 2024年7月25日 下午2:10
下一篇 2024年8月6日 下午2:38

相关推荐