前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[科普] Service Worker 入门指南

[科普] Service Worker 入门指南

作者头像
Tecvan
发布2022-05-18 09:43:36
1.8K0
发布2022-05-18 09:43:36
举报
文章被收录于专栏:TecvanTecvan

来自 「何天翔」 同学的内部分享。

Service Worker 简介

Service Workers 本质上是一种能在浏览器后台运行的独立线程,它能够在网页关闭后持续运行,能够拦截网络请求并根据网络是否可用来采取适当的动作、更新来自服务器的的资源,从而实现拦截和加工网络请求、消息推送、静默更新、事件同步等一系列功能,是 PWA 应用的核心技术之一。

与普通 JS 运行环境相比,Service Workers 有如下特点:

  • 无法直接访问 DOM , 可通过 postMessage 发送消息与页面通信;
  • 能够控制页面发送网络请求;
  • 必须在 HTTPS 协议下运行;
  • 开发过程中可以通过 locakhost 使用 service worker。

本文将从应用角度,简单汇总下 Service Workers 几个核心概念,包括:API、生命周期、waitUntil 机制、调试等。

生命周期

Service Worker 的生命周期完全独立于网页。生命周期 (install -> waiting -> activate -> fetch):

其中, install 事件是 Service Worker 获取的第一个事件,并且只发生一次。

主要逻辑 & API

  • register
  • install
  • activate
  • fetch
  • skipWaiting
代码语言:javascript
复制
  // register
  if ('serviceWorker' in navigator) {
        // 由于 127.0.0.1:8000 是所有测试 Demo 的 host
        // 为了防止作用域污染,将安装前注销所有已生效的 Service Worker
        navigator.serviceWorker.getRegistrations()
          .then(regs => {
            for (let reg of regs) {
              reg.unregister()
            }
            navigator.serviceWorker.register('./sw.js')
          })
      }
代码语言:javascript
复制
// sw.js
console.log('service worker 注册成功')

self.addEventListener('install', () => {
  // 安装回调的逻辑处理
  console.log('service worker 安装成功')
})

self.addEventListener('activate', () => {
  // 激活回调的逻辑处理
  console.log('service worker 激活成功')
})

self.addEventListener('fetch', event => {
  console.log('service worker 抓取请求成功: ' + event.request.url)
})

「waitUntil 机制」

参考:https://developer.mozilla.org/zh-CN/docs/Web/API/ExtendableEvent/waitUntil

ExtendableEvent.waitUntil() 方法告诉事件分发器该事件仍在进行。这个方法也可以用于检测进行的任务是否成功。在服务工作线程中,这个方法告诉浏览器事件一直进行,直至 promise resolve,浏览器不应该在事件中的异步操作完成之前终止服务工作线程。

「skipWaiting」

Service Worker 一旦更新,需要等所有的终端都关闭之后,再重新打开页面才能激活新的 Service Worker,这个过程太复杂了。通常情况下,开发者希望当 Service Worker 一检测到更新就直接激活新的 Service Worker。如果不想等所有的终端都关闭再打开的话,只能通过 skipWaiting 的方法了。

Service Worker 在全局提供了一个 skipWaiting() 方法,skipWaiting() 在 waiting 期间调用还是在之前调用并没有什么不同。一般情况下是在 install 事件中调用它。

「clients.claim() 方法」

如果使用了 skipWaiting 的方式跳过 waiting 状态,直接激活了 Service Worker,可能会出现其他终端还没有受当前终端激活的 Service Worker 控制的情况,切回其他终端之后,Service Worker 控制页面的效果可能不符合预期,尤其是如果 Service Worker 需要动态拦截第三方请求的时候。

为了保证 Service Worker 激活之后能够马上作用于所有的终端,通常在激活 Service Worker 后,通过在其中调用 self.clients.claim() 方法控制未受控制的客户端。self.clients.claim() 方法返回一个 Promise,可以直接在 waitUntil() 方法中调用,如下代码所示:

代码语言:javascript
复制
self.addEventListener('activate', event => {
  event.waitUntil(
    self.clients.claim()
      .then(() => {
        // 返回处理缓存更新的相关事情的 Promise
      })
  )
})

如何处理 Service Worker 的更新

  • 如果目前尚未有活跃的 SW ,那就直接安装并激活。
  • 如果已有 SW 安装着,向新的 swUrl 发起请求,获取内容和和已有的 SW 比较。如没有差别,则结束安装。如有差别,则安装新版本的 SW(执行 install 阶段),之后令其等待(进入 waiting 阶段)
  • 如果老的 SW 控制的所有页面 「全部关闭」,则老的 SW 结束运行,转而激活新的 SW(执行 activated 阶段),使之接管页面。

方法一:skipWaiting

问题:同一个页面,前半部分的请求是由 sw.v1.js 控制,而后半部分是由 sw.v2.js 控制。这两者的不一致性很容易导致问题,甚至网页报错崩溃

方法二:skipWaiting + 刷新

代码语言:javascript
复制
let refreshing = false
navigator.serviceWorker.addEventListener('controllerchange', () => {
  if (refreshing) {
    return
  }
  refreshing = true;
  window.location.reload();
});

问题:毫无征兆的刷新页面的确不可接受,影响用户体验

方法三:给用户一个提示

大致的流程是:

  1. 浏览器检测到存在新的(不同的)SW 时,安装并让它等待,同时触发 updatefound 事件
  2. 我们监听事件,弹出一个提示条,询问用户是不是要更新 SW
  3. 如果用户确认,则向处在等待的 SW 发送消息,要求其执行 skipWaiting 并取得控制权
  4. 因为 SW 的变化触发 controllerchange 事件,我们在这个事件的回调中刷新页面即可

问题:

  • 弊端一:过于复杂
  • 弊端二:刷新逻辑的实现必须通过 JS 完成更新

如何调试

为了更熟练的运用 Chrome Devtools 调试 Service Worker,首先需要熟悉以下这些选项:

  • 「Offline」:复选框可以将 DevTools 切换至离线模式。它等同于 Network 窗格中的离线模式。
  • 「Update on reload」:复选框可以强制 Service Worker 线程在每次页面加载时更新。
  • 「Bypass for network」:复选框可以绕过 Service Worker 线程并强制浏览器转至网络寻找请求的资源。
  • 「Update」:按钮可以对指定的 Service Worker 线程执行一次性更新。
  • 「Push」:按钮可以在没有负载的情况下模拟推送通知。
  • 「Sync」:按钮可以模拟后台同步事件。
  • 「Unregister」:按钮可以注销指定的 Service Worker 线程。
  • 「Source」:告诉当前正在运行的 Service Worker 线程的安装时间,链接是 Service Worker 线程源文件的名称。点击链接会将定向并跳转至 Service Worker 线程来源。
  • 「Status」:告诉 Service Worker 线程的状态。此行上的数字指示 Service Worker 线程已被更新的次数。如果启用 update on reload 复选框,接下来会注意到每次页面加载时此数字都会增大。在状态旁边会看到 start 按钮(如果 Service Worker 线程已停止)或 stop 按钮(如果 Service Worker 线程正在运行)。Service Worker 线程设计为可由浏览器随时停止和启动。使用 stop 按钮明确停止 Service Worker 线程可以模拟这一点。停止 Service Worker 线程是测试 Service Worker 线程再次重新启动时的代码行为方式的绝佳方法。它通常可以揭示由于对持续全局状态的不完善假设而引发的错误。
  • 「Clients」:告诉 Service Worker 线程作用域的原点。如果已启用 show all 复选框,focus 按钮将非常实用。在此复选框启用时,系统会列出所有注册的 Service Worker 线程。如果这时候点击正在不同标签中运行的 Service Worker 线程旁的 focus 按钮,Chrome 会聚焦到该标签。

总结

完整流程

应用场景

基于service worker 可以实现拦截和处理网络请求、消息推送、静默更新、事件同步等服务。

  1. 离线缓存:配合 CacheStorage 可以将应用中不变化的资源或者很少变化的资源长久的存储在用户端,提升加载速度、降低流量消耗、降低服务器压力,提高请求速度,让用户体验更加丝滑
  2. 消息推送:激活沉睡的用户,推送即时消息、公告通知,激发更新等。如web资讯客户端、web即时通讯工具、h5游戏等运营产品。
  3. 事件同步:确保web端产生的任务即使在用户关闭了web页面也可以顺利完成。如web邮件客户端、web即时通讯工具等。
  4. 定时同步:周期性的触发Service Worker脚本中的定时同步事件,可借助它提前刷新缓存内容
  5. 结合CacheStorage、 Push API 和 Notification API

参考链接:

  • https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API
  • https://juejin.cn/post/6844903792522035208
  • https://www.simicart.com/blog/pwa-service-worker/
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-04-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Tecvan 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Service Worker 简介
  • 生命周期
  • 主要逻辑 & API
  • 「waitUntil 机制」
    • 「skipWaiting」
      • 「clients.claim() 方法」
      • 如何处理 Service Worker 的更新
        • 方法一:skipWaiting
          • 方法二:skipWaiting + 刷新
            • 方法三:给用户一个提示
            • 如何调试
            • 总结
              • 完整流程
                • 应用场景
                相关产品与服务
                即时通信 IM
                即时通信 IM(Instant Messaging)基于腾讯二十余年的 IM 技术积累,支持Android、iOS、Mac、Windows、Web、H5、小程序平台且跨终端互通,低代码 UI 组件助您30分钟集成单聊、群聊、关系链、消息漫游、群组管理、资料管理、直播弹幕和内容审核等能力。适用于直播互动、电商带货、客服咨询、社交沟通、在线课程、企业办公、互动游戏、医疗健康等场景。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档