精华内容
下载资源
问答
  • Upgrade to Workbox v4

    2020-11-27 13:39:50
    <div><p>Workbox 4.0 just came out which is exciting. I'd love to upgrade <code>next-offline</code> once stable drops. <p>https://github.com/GoogleChrome/workbox/releases/tag/v4.0.0-alpha.0</p><p>...
  • workbox-tutorial-v4-源码

    2021-03-09 15:48:48
    Workbox v4教程 关于 Magazine的完整文章: 设定说明 git clone git@github.com:jadjoubran/workbox-tutorial-v4.git cd workbox-tutorial-v4 npm install npm run dev 然后在浏览器中导航到 。 解决方案 解决方案...
  • Updates workbox to v4

    2020-12-04 14:38:11
    <p>This PR updates our <code>workbox-webpack-plugin</code> to major version 4. <h2>Related Issue <p>Closes #1060 . <h2>Verification Steps <h3>Build should succeed: <pre><code>bash yarn clean:...
  • Workbox 目前发了一个大版本,从 v3.x 到了 v4.x,变化有挺大的,下面是在 window 环境下的模块。 什么是 workbox-window? workbox-window 包是一组模块,用于在 window 上下文中运行,也就是说,在你的网页内部...

    Workbox 目前发了一个大版本,从 v3.x 到了 v4.x,变化有挺大的,下面是在 window 环境下的模块。


    什么是 workbox-window?

    workbox-window 包是一组模块,用于在 window 上下文中运行,也就是说,在你的网页内部运行。 它们是 servicewoker 中运行的其他 workbox 的补充。

    workbox-window的主要功能/目标是:

    • 通过帮助开发人员确定 serviceWorker 生命周期中最关键的时刻,并简化对这些时刻的响应,简化 serviceWoker 注册和更新的过程。
    • 帮助防止开发人员犯下最常见的错误。
    • 使 serviceWorker 程序中运行的代码与 window 中运行的代码之间的通信更加轻松。

    导入和使用 workbox-window

    workbox-window 包的主要入口点是 Workbox 类,你可以从我们的CDN或使用任何流行的 JavaScript 打包工具将其导入代码中。

    使用我们的 CDN

    在您的网站上导入 Workbox 类的最简单方法是从我们的 CDN:

    <script type="module">
    import {Workbox} from 'https://storage.googleapis.com/workbox-cdn/releases/4.0.0/workbox-window.prod.mjs';
    
    if ('serviceWorker' in navigator) {
      const wb = new Workbox('/sw.js');
    
      wb.register();
    }
    </script>
    

    注意,此示例使用 <script type ="module">import 语句来加载 Workbox 类。 虽然您可能认为需要转换此代码以使其在旧版浏览器中运行,但实际上并不是必需的。

    支持 serviceWorker 的所有主要浏览器也支持 JavaScript 模块,因此将此代码提供给任何浏览器都是完美的(旧版浏览器将忽略它)。

    通过 JavScript 打包加载 Workbox

    虽然使用 Workbox 绝对不需要工具,但如果您的开发基础架构已经包含了与 npm 依赖项一起使用的 webpackRollup 等打包工具,则可以使用它们来加载 Workbox

    第一步就是安装 Workbox 做为你应用的依赖:

    npm install workbox-window
    

    然后,在您的某个应用程序的 JavaScript 文件中,通过引用 workbox-window 包名称导入 Workbox

    import {Workbox} from 'workbox-window';
    
    if ('serviceWorker' in navigator) {
      const wb = new Workbox('/sw.js');
    
      wb.register();
    }
    

    如果您的打包工具支持通过动态 import 语句进行代码拆分,你还可以有条件地加载workbox-window,这有助于减少页面主包的大小。

    尽管 Workbox 非常小(1kb gzip压缩),但是没有理由需要加载站点的核心应用程序逻辑,因为 serviceWorker 本质上是渐进式增强。

    if ('serviceWorker' in navigator) {
      const {Workbox} = await import('workbox-window');
    
      const wb = new Workbox('/sw.js');
      wb.register();
    }
    

    高级打包概念

    与在 Service worker 中运行的 Workbox 包不同,workbox-windowpackage.json 中的 mainmodule 字段引用的构建文件被转换为 ES5。 这使它们与当今的构建工具兼容 - 其中一些不允许开发人员转换其 node_module 依赖项的任何内容。

    如果你的构建系统允许您转换依赖项(或者如果您不需要转换任何代码),那么最好导入特定的源文件而不是包本身。

    以下是你可以导入 Workbox 的各种方法,以及每个方法将返回的内容的说明:

    // 使用ES5语法导入UMD版本
    // (pkg.main: "build/workbox-window.prod.umd.js")
    const {Workbox} = require('workbox-window');
    
    // 使用ES5语法导入模块版本
    // (pkg.module: "build/workbox-window.prod.es5.mjs")
    import {Workbox} from 'workbox-window';
    
    // 使用ES2015 +语法导入模块源文件
    import {Workbox} from 'workbox-window/Workbox.mjs';
    

    重要! 如果您直接导入源文件,则还需要配置构建过程以缩小文件,并在将其部署到生产时删除仅开发代码。 有关详细信息,请参阅使用打包(webpack / Rollup)和Workbox的指南

    示例

    导入 Workbox 类后,可以使用它来注册 serviceWorker 并与之交互。 以下是您可以在应用程序中使用 Workbox 的一些示例:

    注册 serviceWorker 并在 serviceWorker 第一次处于 active 状态时通知用户:

    许多 Web 应用程序用户 serviceWorker 预缓存资源,以便其应用程序在后续页面加载时离线工作。在某些情况下,通知用户该应用程序现在可以离线使用是有意义的。

    const wb = new Workbox('/sw.js');
    
    wb.addEventListener('activated', (event) => {
      // 如果另一个版本的 serviceWorker,`event.isUpdate`将为true
      // 当这个版本注册时,worker 正在控制页面。
      if (!event.isUpdate) {
        console.log('Service worker 第一次激活!');
    
        // 如果您的 serviceWorker 配置为预缓存资源,那么
        // 资源现在都应该可用。
      }
    });
    
    // 添加事件侦听器后注册 serviceWorker 。
    wb.register();
    

    如果 serviceWorker 已安装但等待激活,则通知用户

    当由现有 serviceWorker 控制的页面注册新的 serviceWorker 时,默认情况下,在初始 serviceWorker 控制的所有客户端完全卸载之前,serviceWorker 将不会激活。

    这是开发人员常见的混淆源,特别是在重新加载当前页面不会导致新 serviceWorker 程序激活的情况下。

    为了帮助减少混淆并在发生这种情况时明确说明,Workbox 类提供了一个可以监听的等待事件:

    const wb = new Workbox('/sw.js');
    
    wb.addEventListener('waiting', (event) => {
      console.log(`已安装新的 serviceWorker,但无法激活` +
          `直到运行当前版本的所有选项卡都已完全卸载。`);
    });
    
    // 添加事件侦听器后注册 service worker 。
    wb.register();
    

    从 workbox-broadcast-update 包通知用户缓存更新

    workbox-broadcast-update 包非常棒

    能够从缓存中提供内容(快速交付)的方式,同时还能够通知用户该内容的更新(使用stale-while-revalidate 策略)。

    要从 window 接收这些更新,您可以侦听 CACHE_UPDATE 类型的消息事件:

    const wb = new Workbox('/sw.js');
    
    wb.addEventListener('message', (event) => {
      if (event.data.type === 'CACHE_UPDATE') {
        const {updatedURL} = event.data.payload;
    
        console.log(`${updatedURL} 的更新版本可用`);
      }
    });
    
    // 添加事件侦听器后注册 service worker。
    wb.register();
    

    向 serviceWorker 发送要缓存的URL列表

    对于某些应用程序,可以知道在构建时需要预先缓存的所有资源,但某些应用程序根据用户首先登陆的 URL 提供完全不同的页面。

    对于后一类别的应用程序,仅缓存用户所访问的特定页面所需的资源可能是有意义的。 使用 workbox-routing 软件包时,您可以向路由器发送一个 URL 列表进行缓存,它将根据路由器本身定义的规则缓存这些 URL。

    每当激活新的 serviceWorker 时,此示例都会将页面加载的 URL 列表发送到路由器。 请注意,发送所有 URL 是可以的,因为只会缓存与 serviceWorker 中定义的路由匹配的 URL:

    const wb = new Workbox('/sw.js');
    
    wb.addEventListener('activated', (event) => {
      // 获取当前页面URL +页面加载的所有资源。
      const urlsToCache = [
        location.href,
        ...performance.getEntriesByType('resource').map((r) => r.name),
      ];
      // 将该URL列表发送到 serviceWorker 的路由器。
      wb.messageSW({
        type: 'CACHE_URLS',
        payload: {urlsToCache},
      });
    });
    
    // 添加事件侦听器后注册 serviceWorker。
    wb.register();
    

    注意:上述技术适用于通过默认路由器上的 workbox.routing.registerRoute() 方法定义的任何路由。 如果您要创建自己的路由器实例,则需要手动调用 addCacheListener() 。

    重要的 serviceWorker 生命周期

    serviceWorker 的生命周期很复杂,完全可以理解。 它之所以如此复杂,部分原因在于它必须处理 serviceWorker 所有可能使用的所有边缘情况(例如,注册多个 serviceWorker,在不同的框架中注册不同的 serviceWorker,注册具有不同名称的 serviceWorker 等)。

    但是大多数实现 serviceWorker 的开发人员不应该担心所有这些边缘情况,因为它们的使用非常简单。 大多数开发人员每页加载只注册一个 serviceWorker,并且他们不会更改他们部署到服务器的 serviceWorker 文件的名称。

    Workbox 类通过将所有 serviceWorker 注册分为两类来包含 serviceWorker 生命周期的这个更简单的视图:实例自己的注册 serviceWorker 和外部 serviceWorker:

    • 注册 serviceWorker:由于 Workbox 实例调用 register() 而已开始安装的 serviceWorker,或者如果调用 register() 未在注册时触发 updatefound 事件,则已启用安装 serviceWorker。
    • 外部 serviceWorker:一个 serviceWorker,开始独立于 Workbox 实例调用 register() 安装。 当用户在另一个标签页中打开新版本的网站时,通常会发生这种情况。

    我们的想法是,来自 serviceWorker 的所有生命周期事件都是你的代码应该期待的事件,而来自外部 serviceWorker 的所有生命周期事件都应该被视为具有潜在危险,并且应该相应地警告用户。

    考虑到这两类 serviceWorker,下面是所有重要serviceWorker 生命周期时刻的细分,以及开发人员如何处理它们的建议:

    第一次安装 serviceWorker

    你可能希望在 serviceWorker 第一次安装时不同于处理所有未来更新的方式。

    在 Workbox 中,你可以通过检查以下任何事件的 isUpdate 属性来区分版本首次安装和未来更新。 对于第一次安装,isUpdate 将为 false。

    const wb = new Workbox('/sw.js');
    
    wb.addEventListener('installed', (event) => {
      if (!event.isUpdate) {
        // 在这里编写第一次安装需要的代码
      }
    });
    
    wb.register();
    
    时刻 事件 建议操作
    新的 serviceWorker 已安装(第一次) installed serviceWorker 第一次安装时,通常会预先缓存网站离线工作所需的所有资源。 你可以考虑通知用户他们的站点现在可以离线运行。

    此外,由于 serviceWorker 第一次安装它时不会截获该页面加载的获取事件,你也可以考虑缓存已加载的资源(尽管如果这些资源已经被预先缓存,则不需要这样做)。 向上面的缓存示例发送 serviceWorker 的URL列表显示了如何执行此操作。
    serviceWorker 已经控制页面 controlling 安装新 serviceWorker 程序并开始控制页面后,所有后续获取事件都将通过该 serviceWorker 程序。 如果你的 serviceWorker 添加了任何特殊逻辑来处理特定的 fetch 事件,那么当你知道逻辑将运行时就是这一点。

    请注意,第一次安装 serviceWorker 时,它不会开始控制当前页面,除非该 serviceWorker 在其 activate 事件中调用 clients.claim()。 默认行为是等到下一页加载开始控制。

    workbox-window 的角度来看,这意味着仅在 serviceWorker 调用 clients.claim() 的情况下才调度 controlling 事件。 如果在注册之前已经控制了页面,则不会调度此事件。
    serviceWorker 已经完成激活 activated 如上所述,serviceWorker 第一次完成激活它可能(或可能不)已经开始控制页面。

    因此,你不应该将 activate 事件视为了解 serviceWorker 何时控制页面的方式。 但是,如果你在活动事件中(在 serviceWorker )运行逻辑,并且你需要知道该逻辑何时完成,则激活的事件将让你知道。

    发现 serviceWorker 的更新版本时

    当新 serviceWorker 开始安装但现有版本当前正在控制该页面时,以下所有事件的 isUpdate 属性都将为 true。

    在这种情况下,你的反应通常与第一次安装不同,因为你必须管理用户何时以及如何获得此更新。

    时刻 事件 建议操作
    已安装新 serviceWorker(更新前一个) installed 如果这不是第一个 serviceWorker 安装(event.isUpdate === true),则表示已找到并安装了较新版本的 serviceWorker(即,与当前控制页面的版本不同)。

    这通常意味着已将更新版本的站点部署到你的服务器,并且新资源可能刚刚完成预先缓存。

    注意:某些开发人员使用已安装的事件来通知用户其新版本的站点可用。 但是,根据我是否在安装 serviceWorker 程序中调用 skipWaiting(),安装的 serviceWorker 可能会立即生效,也可能不会立即生效。 如果你确实调用 skipWaiting(),那么最好在新 serviceWorker 激活后通知用户更新,如果你没有调用 skipWaiting,最好通知他们等待事件中的挂起更新(见下文了解更多信息) 细节)。
    serviceWorker 已安装,但它仍处于等待阶段 waiting 如果 serviceWorker 的更新版本在安装时未调用skipWaiting(),则在当前活动 serviceWorker 控制的所有页面都已卸载之前,它不会激活。 你可能希望通知用户更新可用,并将在下次访问时应用。

    警告! 开发人员通常会提示用户重新加载以获取更新,但在许多情况下刷新页面不会激活已安装的工作程序。 如果用户刷新页面并且serviceWorker 仍在等待,则等待事件将再次触发,并且 event.wasWaitingBeforeRegister 属性将为 true。 请注意,我们计划在将来的版本中改进此体验。 关注问题#1848以获取更新。

    另一种选择是提示用户并询问他们是否想要获得更新或继续等待。 如果选择获取更新,则可以使用 postMessage() 告诉 serviceWorker 运行 skipWaiting()。 有关示例,请参阅高级配方为用户提供页面重新加载。
    serviceWorker 已开始控制页面 controlling 当更新的 serviceWorker 开始控制页面时,这意味着当前控制的 serviceWorker 的版本与加载页面时控制的版本不同。 在某些情况下可能没问题,但也可能意味着当前页面引用的某些资源不再位于缓存中(也可能不在服务器上)。 你可能需要考虑通知用户页面的某些部分可能无法正常工作。

    注意:如果不在serviceWorker 中调用 skipWaiting(),则不会触发控制事件。
    serviceWorker 已完成激活 activated 当更新的 serviceWorker 完成激活时,这意味着你在 serviceWorker 的激活中运行的任何逻辑都已完成。 如果有什么需要延迟,直到逻辑完成,这是运行它的时间。

    找到意外版本的 serviceWorker

    有时用户会在很长一段时间内在后台标签中打开你的网站。 他们甚至可能会打开一个新标签并导航到你的网站,却没有意识到他们已经在后台标签中打开了您的网站。 在这种情况下,您的网站可能同时运行两个版本,这可能会为开发人员带来一些有趣的问题。

    考虑这样一种情况,即您的网站的标签 A 正在运行 v1,标签 B 正在运行 v2。 加载选项卡 B 时,它将由 v1 附带的 serviceWorker 版本控制,但服务器返回的页面(如果使用网络优先缓存策略用于导航请求)将包含所有 v2 资源。

    这对于选项卡 B 来说通常不是问题,因为当你编写 v2 代码时,你知道你的 v1 代码是如何工作的。但是,它可能是标签A的问题,因为你的 v1 代码无法预测你的 v2 代码可能会引入哪些更改。

    为了帮助处理这些情况,workbox-window 还会在检测到来自“外部” serviceWorker 的更新时调度生命周期事件,其中 external 表示任何不是当前 Workbox 实例注册的版本。

    时刻 事件 建议操作
    已安装外部 serviceWorker externalinstalled 如果已安装外部 serviceWorker,则可能意味着用户在不同的选项卡中运行你网站的较新版本。

    如何响应可能取决于已安装的服务是进入等待还是活动阶段。
    通过等待激活来安装外部 serviceWorker externalwaiting 如果外部 serviceWorker 正在等待激活,则可能意味着用户试图在另一个选项卡中获取你网站的新版本,但是由于此选项卡仍处于打开状态,因此他们已被阻止。

    如果发生这种情况,你可以考虑向用户显示通知,要求他们关闭此标签。 在极端情况下,你甚至可以考虑调用 window.reload(),如果这样做不会导致用户丢失任何已保存的状态。
    serviceWorker 外部 serviceWorker 已激活 externalactivated 如果外部 serviceWorker 程序已激活,则当前页面很可能无法继续正常运行。 你可能需要考虑向用户显示他们正在运行旧版本页面的通知,并且可能会出现问题。

    避免常见错误

    Workbox 提供的最有用的功能之一是它的开发人员日志记录。 对于 worbox-window 也是这样。

    我们知道与 serviceWorker 一起开发往往会让人感到困惑,当事情发生与你期望的相反时,很难知道原因。

    例如,当你对 serviceWorker 进行更改并重新加载页面时,你可能无法在浏览器中看到该更改。 最可能的原因是,你的 serviceWorker 仍在等待激活。

    但是当使用 Workbox 类注册 serviceWorker 时,你将被告知开发人员控制台中的所有生命周期状态更改,这应该有助于调试为什么事情不像你期望的那样。

    此外,开发人员在首次使用 serviceWorker 时常犯的错误是在错误的范围内注册 serviceWorker。

    为了防止这种情况发生,Workbox类将警告您注册服务工作者的页面是否不在该服务工作者的范围内。 如果您的服务工作者处于活动状态但尚未控制该页面,它还会警告你:

    window 到 serviceWorker 的沟通

    大多数高级 serviceWorker 使用涉及 serviceWorker 和 window 之间的消息传递丢失。 Workbox 类通过提供 messageSW() 方法来帮助解决这个问题,该方法将postMessage() 实例的注册 serviceWorker 并等待响应。

    虽然你可以以任何格式向 serviceWorker 发送数据,但所有 Workbox 包共享的格式是具有三个属性的对象(后两个是可选的):

    属性 必须 类型 描述
    type string 标识此消息的唯一字符串。

    按照惯例,类型都是大写的,下划线分隔单词。 如果类型表示要采取的动作,则它应该是现在时的命令(例如 CACHE_URLS ),如果类型表示报告的信息,则它应该是过去时(例如 URLS_CACHED )。
    meta string 在 Workbox 中,这始终是发送消息的 Workbox 包的名称。 自己发送邮件时,可以省略此属性或将其设置为你喜欢的任何内容。
    payload * 正在发送的数据。 通常这是一个对象,但它不一定是。

    通过 messageSW() 方法发送的消息使用 MessageChannel,因此接收方可以响应它们。 要响应消息,你可以在消息事件侦听器中调用 event.ports[0].postMessage(response)。 messageSW() 方法返回一个 promise,该 promise 将解析为你返回的任何响应。

    这是一个从 window 到 serviceWorker 发送消息并获得响应的示例。 第一个代码块是 serviceWorker 中的消息侦听器,第二个块使用 Workbox 类发送消息并等待响应:

    sw.js 中的代码:

    const SW_VERSION = '1.0.0';
    
    addEventListener('message', (event) => {
      if (event.data.type === 'GET_VERSION') {
        event.ports[0].postMessage(SW_VERSION);
      }
    });
    

    main.js 中的代码(运行在 window 环境):

    const wb = new Workbox('/sw.js');
    wb.register();
    
    const swVersion = await wb.messageSW({type: 'GET_VERSION'});
    console.log('Service Worker version:', swVersion);
    

    管理版本不兼容性

    上面的示例显示了如何从 window 中实现检查 serviceWorker 版本。 使用此示例是因为当你在 window 和 serviceWorker 之间来回发送消息时,请务必注意你的 serviceWorker 可能没有运行与你的页面代码运行相同的站点版本,以及 处理此问题的解决方案会有所不同,具体取决于你是以网络优先服务还是缓存优先服务。

    网络优先

    首先为你的网页提供服务时,你的用户将始终从你的服务器获取最新版本的 HTML。 但是,当用户第一次重新访问你的站点时(在部署更新之后),他们获得的 HTML 将是最新版本,但在其浏览器中运行的 serviceWorker 将是先前安装的版本(可能是许多旧版本)。

    理解这种可能性非常重要,因为如果当前版本的页面加载的 JavaScript 向旧版本的 serviceWorker 发送消息,则该版本可能不知道如何响应(或者它可能以不兼容的格式响应)。

    因此,在进行任何关键工作之前,始终对 serviceWorker 进行版本控制并检查兼容版本是个好主意。

    例如,在上面的代码中,如果该 messageSW() 调用返回的 serviceWorker 版本早于预期版本,则最好等到找到更新(这应该在调用 register() 时发生)。 此时,你可以通知用户或更新,也可以手动跳过等待阶段以立即激活新的 serviceWorker。

    缓存优先

    与在网络服务页面时相比,首先,当你首先提供页面缓存时,你知道你的页面最初将始终与 serviceWorker 的版本相同(因为这是服务它的原因)。 因此,立即使用messageSW() 是安全的。

    但是,如果找到 serviceWorker 的更新版本并在页面调用 register() 时激活(即你有意跳过等待阶段),则向其发送消息可能不再安全。

    管理这种可能性的一种策略是使用版本控制方案,允许你区分中断更新和非中断更新,并且在更新中断的情况下,你知道向 serviceWorker 发送消息是不安全的。 相反,你需要警告用户他们正在运行旧版本的页面,并建议他们重新加载以获取更新。


    博客名称:王乐平博客

    CSDN博客地址:http://blog.csdn.net/lecepin

    知识共享许可协议
    本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。
    展开全文
  • Update Workbox to v5.0.0

    2020-11-27 17:51:47
    <div><p>...<p>Migrate from Workbox v4 to v5: https://developers.google.com/web/tools/workbox/guides/migrations/migrate-from-v4</p><p>该提问来源于开源项目:open-wc/open-wc</p></div>
  • Workbox 目前发了一个大版本,从 v3.x 到了 v4.x,变化有挺大的,下面是在 window 环境下的模块。 什么是 workbox-window? workbox-window 包是一组模块,用于在 window 上下文中运行,也就是说,在你的网页内部...

    Workbox 目前发了一个大版本,从 v3.x 到了 v4.x,变化有挺大的,下面是在 window 环境下的模块。


    什么是 workbox-window?

    workbox-window 包是一组模块,用于在 window 上下文中运行,也就是说,在你的网页内部运行。 它们是 servicewoker 中运行的其他 workbox 的补充。

    workbox-window的主要功能/目标是:

    • 通过帮助开发人员确定 serviceWorker 生命周期中最关键的时刻,并简化对这些时刻的响应,简化 serviceWoker 注册和更新的过程。
    • 帮助防止开发人员犯下最常见的错误。
    • 使 serviceWorker 程序中运行的代码与 window 中运行的代码之间的通信更加轻松。

    导入和使用 workbox-window

    workbox-window 包的主要入口点是 Workbox 类,你可以从我们的CDN或使用任何流行的 JavaScript 打包工具将其导入代码中。

    使用我们的 CDN

    在您的网站上导入 Workbox 类的最简单方法是从我们的 CDN:

    <script type="module">
    import {Workbox} from 'https://storage.googleapis.com/workbox-cdn/releases/4.0.0/workbox-window.prod.mjs';
    
    if ('serviceWorker' in navigator) {
      const wb = new Workbox('/sw.js');
    
      wb.register();
    }
    </script>
    复制代码

    注意,此示例使用 <script type ="module">import 语句来加载 Workbox 类。 虽然您可能认为需要转换此代码以使其在旧版浏览器中运行,但实际上并不是必需的。

    支持 serviceWorker 的所有主要浏览器也支持 JavaScript 模块,因此将此代码提供给任何浏览器都是完美的(旧版浏览器将忽略它)。

    通过 JavScript 打包加载 Workbox

    虽然使用 Workbox 绝对不需要工具,但如果您的开发基础架构已经包含了与 npm 依赖项一起使用的 webpackRollup 等打包工具,则可以使用它们来加载 Workbox

    第一步就是安装 Workbox 做为你应用的依赖:

    npm install workbox-window
    复制代码

    然后,在您的某个应用程序的 JavaScript 文件中,通过引用 workbox-window 包名称导入 Workbox

    import {Workbox} from 'workbox-window';
    
    if ('serviceWorker' in navigator) {
      const wb = new Workbox('/sw.js');
    
      wb.register();
    }
    复制代码

    如果您的打包工具支持通过动态 import 语句进行代码拆分,你还可以有条件地加载workbox-window,这有助于减少页面主包的大小。

    尽管 Workbox 非常小(1kb gzip压缩),但是没有理由需要加载站点的核心应用程序逻辑,因为 serviceWorker 本质上是渐进式增强。

    if ('serviceWorker' in navigator) {
      const {Workbox} = await import('workbox-window');
    
      const wb = new Workbox('/sw.js');
      wb.register();
    }
    复制代码

    高级打包概念

    与在 Service worker 中运行的 Workbox 包不同,workbox-windowpackage.json 中的 mainmodule 字段引用的构建文件被转换为 ES5。 这使它们与当今的构建工具兼容 - 其中一些不允许开发人员转换其 node_module 依赖项的任何内容。

    如果你的构建系统允许您转换依赖项(或者如果您不需要转换任何代码),那么最好导入特定的源文件而不是包本身。

    以下是你可以导入 Workbox 的各种方法,以及每个方法将返回的内容的说明:

    // 使用ES5语法导入UMD版本
    // (pkg.main: "build/workbox-window.prod.umd.js")
    const {Workbox} = require('workbox-window');
    
    // 使用ES5语法导入模块版本
    // (pkg.module: "build/workbox-window.prod.es5.mjs")
    import {Workbox} from 'workbox-window';
    
    // 使用ES2015 +语法导入模块源文件
    import {Workbox} from 'workbox-window/Workbox.mjs';
    复制代码

    重要! 如果您直接导入源文件,则还需要配置构建过程以缩小文件,并在将其部署到生产时删除仅开发代码。 有关详细信息,请参阅使用打包(webpack / Rollup)和Workbox的指南

    示例

    导入 Workbox 类后,可以使用它来注册 serviceWorker 并与之交互。 以下是您可以在应用程序中使用 Workbox 的一些示例:

    注册 serviceWorker 并在 serviceWorker 第一次处于 active 状态时通知用户:

    许多 Web 应用程序用户 serviceWorker 预缓存资源,以便其应用程序在后续页面加载时离线工作。在某些情况下,通知用户该应用程序现在可以离线使用是有意义的。

    const wb = new Workbox('/sw.js');
    
    wb.addEventListener('activated', (event) => {
      // 如果另一个版本的 serviceWorker,`event.isUpdate`将为true
      // 当这个版本注册时,worker 正在控制页面。
      if (!event.isUpdate) {
        console.log('Service worker 第一次激活!');
    
        // 如果您的 serviceWorker 配置为预缓存资源,那么
        // 资源现在都应该可用。
      }
    });
    
    // 添加事件侦听器后注册 serviceWorker 。
    wb.register();
    复制代码

    如果 serviceWorker 已安装但等待激活,则通知用户

    当由现有 serviceWorker 控制的页面注册新的 serviceWorker 时,默认情况下,在初始 serviceWorker 控制的所有客户端完全卸载之前,serviceWorker 将不会激活。

    这是开发人员常见的混淆源,特别是在重新加载当前页面不会导致新 serviceWorker 程序激活的情况下。

    为了帮助减少混淆并在发生这种情况时明确说明,Workbox 类提供了一个可以监听的等待事件:

    const wb = new Workbox('/sw.js');
    
    wb.addEventListener('waiting', (event) => {
      console.log(`已安装新的 serviceWorker,但无法激活` +
          `直到运行当前版本的所有选项卡都已完全卸载。`);
    });
    
    // 添加事件侦听器后注册 service worker 。
    wb.register();
    复制代码

    从 workbox-broadcast-update 包通知用户缓存更新

    workbox-broadcast-update 包非常棒

    能够从缓存中提供内容(快速交付)的方式,同时还能够通知用户该内容的更新(使用stale-while-revalidate 策略)。

    要从 window 接收这些更新,您可以侦听 CACHE_UPDATE 类型的消息事件:

    const wb = new Workbox('/sw.js');
    
    wb.addEventListener('message', (event) => {
      if (event.data.type === 'CACHE_UPDATE') {
        const {updatedURL} = event.data.payload;
    
        console.log(`${updatedURL} 的更新版本可用`);
      }
    });
    
    // 添加事件侦听器后注册 service worker。
    wb.register();
    复制代码

    向 serviceWorker 发送要缓存的URL列表

    对于某些应用程序,可以知道在构建时需要预先缓存的所有资源,但某些应用程序根据用户首先登陆的 URL 提供完全不同的页面。

    对于后一类别的应用程序,仅缓存用户所访问的特定页面所需的资源可能是有意义的。 使用 workbox-routing 软件包时,您可以向路由器发送一个 URL 列表进行缓存,它将根据路由器本身定义的规则缓存这些 URL。

    每当激活新的 serviceWorker 时,此示例都会将页面加载的 URL 列表发送到路由器。 请注意,发送所有 URL 是可以的,因为只会缓存与 serviceWorker 中定义的路由匹配的 URL:

    const wb = new Workbox('/sw.js');
    
    wb.addEventListener('activated', (event) => {
      // 获取当前页面URL +页面加载的所有资源。
      const urlsToCache = [
        location.href,
        ...performance.getEntriesByType('resource').map((r) => r.name),
      ];
      // 将该URL列表发送到 serviceWorker 的路由器。
      wb.messageSW({
        type: 'CACHE_URLS',
        payload: {urlsToCache},
      });
    });
    
    // 添加事件侦听器后注册 serviceWorker。
    wb.register();
    复制代码

    注意:上述技术适用于通过默认路由器上的 workbox.routing.registerRoute() 方法定义的任何路由。 如果您要创建自己的路由器实例,则需要手动调用 addCacheListener() 。

    重要的 serviceWorker 生命周期

    serviceWorker 的生命周期很复杂,完全可以理解。 它之所以如此复杂,部分原因在于它必须处理 serviceWorker 所有可能使用的所有边缘情况(例如,注册多个 serviceWorker,在不同的框架中注册不同的 serviceWorker,注册具有不同名称的 serviceWorker 等)。

    但是大多数实现 serviceWorker 的开发人员不应该担心所有这些边缘情况,因为它们的使用非常简单。 大多数开发人员每页加载只注册一个 serviceWorker,并且他们不会更改他们部署到服务器的 serviceWorker 文件的名称。

    Workbox 类通过将所有 serviceWorker 注册分为两类来包含 serviceWorker 生命周期的这个更简单的视图:实例自己的注册 serviceWorker 和外部 serviceWorker:

    • 注册 serviceWorker:由于 Workbox 实例调用 register() 而已开始安装的 serviceWorker,或者如果调用 register() 未在注册时触发 updatefound 事件,则已启用安装 serviceWorker。
    • 外部 serviceWorker:一个 serviceWorker,开始独立于 Workbox 实例调用 register() 安装。 当用户在另一个标签页中打开新版本的网站时,通常会发生这种情况。

    我们的想法是,来自 serviceWorker 的所有生命周期事件都是你的代码应该期待的事件,而来自外部 serviceWorker 的所有生命周期事件都应该被视为具有潜在危险,并且应该相应地警告用户。

    考虑到这两类 serviceWorker,下面是所有重要serviceWorker 生命周期时刻的细分,以及开发人员如何处理它们的建议:

    第一次安装 serviceWorker

    你可能希望在 serviceWorker 第一次安装时不同于处理所有未来更新的方式。

    在 Workbox 中,你可以通过检查以下任何事件的 isUpdate 属性来区分版本首次安装和未来更新。 对于第一次安装,isUpdate 将为 false。

    const wb = new Workbox('/sw.js');
    
    wb.addEventListener('installed', (event) => {
      if (!event.isUpdate) {
        // 在这里编写第一次安装需要的代码
      }
    });
    
    wb.register();
    复制代码
    时刻 事件 建议操作
    新的 serviceWorker 已安装(第一次) installed serviceWorker 第一次安装时,通常会预先缓存网站离线工作所需的所有资源。 你可以考虑通知用户他们的站点现在可以离线运行。

    此外,由于 serviceWorker 第一次安装它时不会截获该页面加载的获取事件,你也可以考虑缓存已加载的资源(尽管如果这些资源已经被预先缓存,则不需要这样做)。 向上面的缓存示例发送 serviceWorker 的URL列表显示了如何执行此操作。
    serviceWorker 已经控制页面 controlling 安装新 serviceWorker 程序并开始控制页面后,所有后续获取事件都将通过该 serviceWorker 程序。 如果你的 serviceWorker 添加了任何特殊逻辑来处理特定的 fetch 事件,那么当你知道逻辑将运行时就是这一点。

    请注意,第一次安装 serviceWorker 时,它不会开始控制当前页面,除非该 serviceWorker 在其 activate 事件中调用 clients.claim()。 默认行为是等到下一页加载开始控制。

    workbox-window 的角度来看,这意味着仅在 serviceWorker 调用 clients.claim() 的情况下才调度 controlling 事件。 如果在注册之前已经控制了页面,则不会调度此事件。
    serviceWorker 已经完成激活 activated 如上所述,serviceWorker 第一次完成激活它可能(或可能不)已经开始控制页面。

    因此,你不应该将 activate 事件视为了解 serviceWorker 何时控制页面的方式。 但是,如果你在活动事件中(在 serviceWorker )运行逻辑,并且你需要知道该逻辑何时完成,则激活的事件将让你知道。

    发现 serviceWorker 的更新版本时

    当新 serviceWorker 开始安装但现有版本当前正在控制该页面时,以下所有事件的 isUpdate 属性都将为 true。

    在这种情况下,你的反应通常与第一次安装不同,因为你必须管理用户何时以及如何获得此更新。

    时刻 事件 建议操作
    已安装新 serviceWorker(更新前一个) installed 如果这不是第一个 serviceWorker 安装(event.isUpdate === true),则表示已找到并安装了较新版本的 serviceWorker(即,与当前控制页面的版本不同)。

    这通常意味着已将更新版本的站点部署到你的服务器,并且新资源可能刚刚完成预先缓存。

    注意:某些开发人员使用已安装的事件来通知用户其新版本的站点可用。 但是,根据我是否在安装 serviceWorker 程序中调用 skipWaiting(),安装的 serviceWorker 可能会立即生效,也可能不会立即生效。 如果你确实调用 skipWaiting(),那么最好在新 serviceWorker 激活后通知用户更新,如果你没有调用 skipWaiting,最好通知他们等待事件中的挂起更新(见下文了解更多信息) 细节)。
    serviceWorker 已安装,但它仍处于等待阶段 waiting 如果 serviceWorker 的更新版本在安装时未调用skipWaiting(),则在当前活动 serviceWorker 控制的所有页面都已卸载之前,它不会激活。 你可能希望通知用户更新可用,并将在下次访问时应用。

    警告! 开发人员通常会提示用户重新加载以获取更新,但在许多情况下刷新页面不会激活已安装的工作程序。 如果用户刷新页面并且serviceWorker 仍在等待,则等待事件将再次触发,并且 event.wasWaitingBeforeRegister 属性将为 true。 请注意,我们计划在将来的版本中改进此体验。 关注问题#1848以获取更新。

    另一种选择是提示用户并询问他们是否想要获得更新或继续等待。 如果选择获取更新,则可以使用 postMessage() 告诉 serviceWorker 运行 skipWaiting()。 有关示例,请参阅高级配方为用户提供页面重新加载。
    serviceWorker 已开始控制页面 controlling 当更新的 serviceWorker 开始控制页面时,这意味着当前控制的 serviceWorker 的版本与加载页面时控制的版本不同。 在某些情况下可能没问题,但也可能意味着当前页面引用的某些资源不再位于缓存中(也可能不在服务器上)。 你可能需要考虑通知用户页面的某些部分可能无法正常工作。

    注意:如果不在serviceWorker 中调用 skipWaiting(),则不会触发控制事件。
    serviceWorker 已完成激活 activated 当更新的 serviceWorker 完成激活时,这意味着你在 serviceWorker 的激活中运行的任何逻辑都已完成。 如果有什么需要延迟,直到逻辑完成,这是运行它的时间。

    找到意外版本的 serviceWorker

    有时用户会在很长一段时间内在后台标签中打开你的网站。 他们甚至可能会打开一个新标签并导航到你的网站,却没有意识到他们已经在后台标签中打开了您的网站。 在这种情况下,您的网站可能同时运行两个版本,这可能会为开发人员带来一些有趣的问题。

    考虑这样一种情况,即您的网站的标签 A 正在运行 v1,标签 B 正在运行 v2。 加载选项卡 B 时,它将由 v1 附带的 serviceWorker 版本控制,但服务器返回的页面(如果使用网络优先缓存策略用于导航请求)将包含所有 v2 资源。

    这对于选项卡 B 来说通常不是问题,因为当你编写 v2 代码时,你知道你的 v1 代码是如何工作的。但是,它可能是标签A的问题,因为你的 v1 代码无法预测你的 v2 代码可能会引入哪些更改。

    为了帮助处理这些情况,workbox-window 还会在检测到来自“外部” serviceWorker 的更新时调度生命周期事件,其中 external 表示任何不是当前 Workbox 实例注册的版本。

    时刻 事件 建议操作
    已安装外部 serviceWorker externalinstalled 如果已安装外部 serviceWorker,则可能意味着用户在不同的选项卡中运行你网站的较新版本。

    如何响应可能取决于已安装的服务是进入等待还是活动阶段。
    通过等待激活来安装外部 serviceWorker externalwaiting 如果外部 serviceWorker 正在等待激活,则可能意味着用户试图在另一个选项卡中获取你网站的新版本,但是由于此选项卡仍处于打开状态,因此他们已被阻止。

    如果发生这种情况,你可以考虑向用户显示通知,要求他们关闭此标签。 在极端情况下,你甚至可以考虑调用 window.reload(),如果这样做不会导致用户丢失任何已保存的状态。
    serviceWorker 外部 serviceWorker 已激活 externalactivated 如果外部 serviceWorker 程序已激活,则当前页面很可能无法继续正常运行。 你可能需要考虑向用户显示他们正在运行旧版本页面的通知,并且可能会出现问题。

    避免常见错误

    Workbox 提供的最有用的功能之一是它的开发人员日志记录。 对于 worbox-window 也是这样。

    我们知道与 serviceWorker 一起开发往往会让人感到困惑,当事情发生与你期望的相反时,很难知道原因。

    例如,当你对 serviceWorker 进行更改并重新加载页面时,你可能无法在浏览器中看到该更改。 最可能的原因是,你的 serviceWorker 仍在等待激活。

    但是当使用 Workbox 类注册 serviceWorker 时,你将被告知开发人员控制台中的所有生命周期状态更改,这应该有助于调试为什么事情不像你期望的那样。

    此外,开发人员在首次使用 serviceWorker 时常犯的错误是在错误的范围内注册 serviceWorker。

    为了防止这种情况发生,Workbox类将警告您注册服务工作者的页面是否不在该服务工作者的范围内。 如果您的服务工作者处于活动状态但尚未控制该页面,它还会警告你:

    window 到 serviceWorker 的沟通

    大多数高级 serviceWorker 使用涉及 serviceWorker 和 window 之间的消息传递丢失。 Workbox 类通过提供 messageSW() 方法来帮助解决这个问题,该方法将postMessage() 实例的注册 serviceWorker 并等待响应。

    虽然你可以以任何格式向 serviceWorker 发送数据,但所有 Workbox 包共享的格式是具有三个属性的对象(后两个是可选的):

    属性 必须 类型 描述
    type string 标识此消息的唯一字符串。

    按照惯例,类型都是大写的,下划线分隔单词。 如果类型表示要采取的动作,则它应该是现在时的命令(例如 CACHE_URLS ),如果类型表示报告的信息,则它应该是过去时(例如 URLS_CACHED )。
    meta string 在 Workbox 中,这始终是发送消息的 Workbox 包的名称。 自己发送邮件时,可以省略此属性或将其设置为你喜欢的任何内容。
    payload * 正在发送的数据。 通常这是一个对象,但它不一定是。

    通过 messageSW() 方法发送的消息使用 MessageChannel,因此接收方可以响应它们。 要响应消息,你可以在消息事件侦听器中调用 event.ports[0].postMessage(response)。 messageSW() 方法返回一个 promise,该 promise 将解析为你返回的任何响应。

    这是一个从 window 到 serviceWorker 发送消息并获得响应的示例。 第一个代码块是 serviceWorker 中的消息侦听器,第二个块使用 Workbox 类发送消息并等待响应:

    sw.js 中的代码:

    const SW_VERSION = '1.0.0';
    
    addEventListener('message', (event) => {
      if (event.data.type === 'GET_VERSION') {
        event.ports[0].postMessage(SW_VERSION);
      }
    });
    复制代码

    main.js 中的代码(运行在 window 环境):

    const wb = new Workbox('/sw.js');
    wb.register();
    
    const swVersion = await wb.messageSW({type: 'GET_VERSION'});
    console.log('Service Worker version:', swVersion);
    复制代码

    管理版本不兼容性

    上面的示例显示了如何从 window 中实现检查 serviceWorker 版本。 使用此示例是因为当你在 window 和 serviceWorker 之间来回发送消息时,请务必注意你的 serviceWorker 可能没有运行与你的页面代码运行相同的站点版本,以及 处理此问题的解决方案会有所不同,具体取决于你是以网络优先服务还是缓存优先服务。

    网络优先

    首先为你的网页提供服务时,你的用户将始终从你的服务器获取最新版本的 HTML。 但是,当用户第一次重新访问你的站点时(在部署更新之后),他们获得的 HTML 将是最新版本,但在其浏览器中运行的 serviceWorker 将是先前安装的版本(可能是许多旧版本)。

    理解这种可能性非常重要,因为如果当前版本的页面加载的 JavaScript 向旧版本的 serviceWorker 发送消息,则该版本可能不知道如何响应(或者它可能以不兼容的格式响应)。

    因此,在进行任何关键工作之前,始终对 serviceWorker 进行版本控制并检查兼容版本是个好主意。

    例如,在上面的代码中,如果该 messageSW() 调用返回的 serviceWorker 版本早于预期版本,则最好等到找到更新(这应该在调用 register() 时发生)。 此时,你可以通知用户或更新,也可以手动跳过等待阶段以立即激活新的 serviceWorker。

    缓存优先

    与在网络服务页面时相比,首先,当你首先提供页面缓存时,你知道你的页面最初将始终与 serviceWorker 的版本相同(因为这是服务它的原因)。 因此,立即使用messageSW() 是安全的。

    但是,如果找到 serviceWorker 的更新版本并在页面调用 register() 时激活(即你有意跳过等待阶段),则向其发送消息可能不再安全。

    管理这种可能性的一种策略是使用版本控制方案,允许你区分中断更新和非中断更新,并且在更新中断的情况下,你知道向 serviceWorker 发送消息是不安全的。 相反,你需要警告用户他们正在运行旧版本的页面,并建议他们重新加载以获取更新。


    博客名称:王乐平博客

    CSDN博客地址:blog.csdn.net/lecepin

    本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。

    转载于:https://juejin.im/post/5c8944c851882536fe67b3a4

    展开全文
  • 目前常见的缓存方式有http缓存、memory cache、disk cahce、localstorage、Service worker缓存等方式,本文介绍的Workbox就是实现Service worker离线缓存的一个工具。 那么问题来了,Service worker离线缓存和传统的...

    将应用中的静态资源缓存是目前最主流的性能优化方法,甚至能让应用秒开!目前常见的缓存方式有http缓存、memory cache、disk cahce、localstorage、Service worker缓存等方式,本文介绍的Workbox就是实现Service worker离线缓存的一个工具。
    那么问题来了,Service worker离线缓存和传统的缓存方式对比,有什么优势和劣势呢,service worker之所以越来越流行,是因为它让前端缓存脱离了服务端,不需要服务端再额外做些什么,前端工程师自己就可以实现缓存,而且缓存内容完全可控,下面是我搜索的几条主流缓存方式的介绍和对比。

    上图从深入理解浏览器缓存处参考
    http缓存依赖于服务端配置,memory cache和disk cache缓存内容不可控,而且只缓存一些静态资源,push cache是临时缓存,localstorage适用于缓存一些全局的数据,对于静态资源很少用它。
    service worker缓存的优缺点:
    优点:

    非侵入式缓存
    缓存内容开发者完全可控
    持续性缓存
    独立于主线程之外,不堵塞进程

    缺点:

    权限太大,能拦截所有fetch请求,需要控制一下
    发版更新处理比较麻烦

    Workbox简介
    Workbox 是 Google Chrome 团队推出的一套 PWA 的解决方案,这套解决方案当中包含了核心库和构建工具,因此我们可以利用 Workbox 实现 Service Worker 的快速开发。
    引入方式
    有两种方式可以引入workbox:
    第一种最为方便,就是通过importScripts()方法从谷歌官方CDN中引入。
    importScripts(‘https://storage.googleapis.com/workbox-cdn/releases/5.0.0/workbox-sw.js’);
    if (workbox) {
    console.log(Yay! Workbox is loaded 🎉);
    } else {
    console.log(Boo! Workbox didn't load 😬);
    }
    复制代码第二种方式就是从本地引入,本地需要从npm库中下载相应的workbox包,然后通过import按需导入,本文的例子就是这种方式。
    import {precaching} from ‘workbox-precaching’;
    import {registerRoute} from ‘workbox-routing’;
    import {BackgroundSyncPlugin} from ‘workbox-background-sync’;

    // 按需引入,然后使用对应模块…
    复制代码详细介绍请查阅官方文档
    配置
    Workbox可以修改缓存名称,可以用setCacheNameDetails设置预缓存和运行时缓存的名称,还可以通过workbox.core.cacheNames.precache 和 workbox.core.cacheNames.runtime 获取当前定义的预缓存和动态缓存名称。
    // 修改默认配置
    workbox.core.setCacheNameDetails({
    prefix: ‘app’,
    suffix: ‘v1’,
    precache: ‘precache’,
    runtime: ‘runtime’
    })

    // 打印修改结果

    // 将打印 ‘app-precache-v1’
    console.log(worbox.core.cacheNames.precache)
    // 将打印 ‘app-runtime-v1’
    console.log(workbox.core.cacheNames.runtime)
    复制代码更多配置下信息请参考官方文配置文档
    预缓存功能
    预缓存功能可以在service worker安装前将一些静态文件提前缓存下来,这样就能保证service worker安装后可以直接存缓存中获取这些静态资源,可以通过以下代码实现。
    import {precacheAndRoute} from ‘workbox-precaching’;

    precacheAndRoute([
    {url: ‘/index.html’, revision: ‘383676’ },
    {url: ‘/styles/app.0c9a31.css’, revision: null},
    {url: ‘/scripts/app.0d5770.js’, revision: null},
    ]);
    复制代码更多预缓存请参考官方预缓存功能文档
    路由功能
    路由功能是workbx的核心功能,主要是匹配资源路径后,采用相应的缓存策略,或者自定义缓存处理,使用方法如下所示:
    import {registerRoute} from ‘workbox-routing’;
    import {CacheFirst} from ‘workbox-strategies’;

    registerRoute( /.(?:png|jpg|jpeg|svg|gif)$/, new CacheFirst({
    cacheName: ‘my-image-cache’,
    }));
    复制代码registerRoute有两个参数,第一个参数是一个正则表达式,用来匹配路径,第二个参数是对匹配路径进行的处理函数,可以用workbox封装的缓存策略处理函数,也可以自定义,上述示例就是使用的workbox内部封装的CacheFirst缓存策略。
    如果第二个参数使用自定义函数,那么这个函数有三个默认参数,示例如下:
    import {registerRoute} from ‘workbox-routing’;

    const handler = async ({url, event, params}) => {
    // Response will be “A guide to Workbox”
    return new Response(A ${params.type} to ${params.name} );
    };
    registerRoute(/.css$/, handler);
    复制代码缓存策略
    Workbox内部封装了以下五种缓存策略:

    NetworkFirst:网络优先
    CacheFirst:缓存优先
    NetworkOnly:仅使用正常的网络请求
    CacheOnly:仅使用缓存中的资源
    StaleWhileRevalidate:从缓存中读取资源的同时发送网络请求更新本地缓存

    五种缓存策略使用方法一致,各适用于不同的场景,具体适用场景可在离线指南中查看。
    Webpack+Workbox构建离线应用
    目前大部分前端项目都离不开webpack,为了方便我们使用workbox,谷歌官方给我们提供了workbox的webpack插件,通过这个插件,我们能在项目中快速引入workbox,通过配置来定制化我们的缓存。
    通过以下四个步骤,我们能将webpack引入到一个由webpack构建的应用中并实现缓存。
    第一步:使用workbox-webpack-plugin

    安装

    npm install workbox-webpack-plugin
    复制代码
    在webpack 配置文件中引入并配置

    workbox-webpack-plugin有两种配置方式:
    第一种:GenerateSW
    通过配置自动在项目中引入service-worker.js,代码如下:
    const WorkboxPlugin = require(‘workbox-webpack-plugin’);

    module.exports = {
    // Other webpack config…
    plugins: [
    // Other plugins…
    new WorkboxPlugin.GenerateSW({
    // Do not precache images
    exclude: [/.(?:png|jpg|jpeg|svg)KaTeX parse error: Can't use function '\.' in math mode at position 205: … urlPattern: /\̲.̲(?:png|jpg|jpeg…/,
    // Apply a cache-first strategy.
    handler: ‘CacheFirst’,
    options: {
    // Use a custom cache name.
    cacheName: ‘images’,
    // Only cache 10 images.
    expiration: {
    maxEntries: 10,
    },
    },
    }],
    })
    ]
    };
    复制代码适用于:

    预缓存静态文件
    只简单的应用运行时的缓存功能

    不适用:

    需要使用service worker 其他功能的场景,如push等
    需要在service worker中导入其他脚本或添加其他逻辑
    具体的配置文件可查阅官方文档,本文示例使用的是第二种方式

    第二种:InjectManifest
    通过已有的service-worker.js文件生成新的service-worker.js,示例如下:
    new workboxPlugin.InjectManifest({
    // 目前的service worker 文件
    swSrc: ‘./src/sw.js’,
    // 打包后生成的service worker文件,一般存到disk目录
    swDest: ‘sw.js’
    })
    复制代码适用于:

    预缓存文件
    更多的定制化缓存需求
    使用service worker其他特性

    如果你只想简单的引入service worker,建议使用第一种方式
    第二步:注册Service Worker
    配置好插件之后,我们需要在项目中注册service worker。
    注册比较简单,只需要在项目入口文件中进行注册即可,代码如下:
    // Check that service workers are supported
    if (‘serviceWorker’ in navigator) {
    // Use the window load event to keep the page load performant
    window.addEventListener(‘load’, () => {
    navigator.serviceWorker.register(’/sw.js’);
    });
    }
    复制代码上述代码是最简单的注册方式,在我们的项目中我们使用register-service-workernpm包注册service worker并添加一下自定义事件,方便后期进行更新和离线事件的处理。
    代码如下:
    import { register } from ‘register-service-worker’;

    export default function(swDest) {
    console.log(“注册sw”)
    register(/${swDest}, {
    ready(registration) {
    // 此方法是我们项目中自己封装的创建自定义事件的公共方法
    dispatchServiceWorkerEvent(‘sw.ready’, registration);
    },
    registered(registration) {
    dispatchServiceWorkerEvent(‘sw.registered’, registration);
    },
    cached(registration) {
    dispatchServiceWorkerEvent(‘sw.cached’, registration);
    },
    updatefound(registration) {
    dispatchServiceWorkerEvent(‘sw.updatefound’, registration);
    },
    updated(registration) {
    dispatchServiceWorkerEvent(‘sw.updated’, registration);
    },
    offline() {
    dispatchServiceWorkerEvent(‘sw.offline’, {});
    },
    error(error) {
    dispatchServiceWorkerEvent(‘sw.error’, error);
    },
    });
    }
    复制代码在入口文件中引入注册文件:
    import registerSW from ‘./sw-register’;
    registerSW(‘sw.js’);
    复制代码第三步:自定义Service Worker缓存配置
    如果我们使用injectMainfest的方式引入servicce worker,需要在src目录下创建一个sw.js(命名自定义,但需要和webpack配置中一致),在这个文件中我们可以进行预缓存等操作。代码示例如下(sw.js):
    import { setCacheNameDetails, clientsClaim } from ‘workbox-core’;
    import { precacheAndRoute } from ‘workbox-precaching/precacheAndRoute’;
    import {createHandlerBoundToURL} from ‘workbox-precaching’;
    import {NavigationRoute, registerRoute} from ‘workbox-routing’;
    import {StaleWhileRevalidate,NetworkOnly} from ‘workbox-strategies’;

    // 设置缓存名称
    setCacheNameDetails({
    prefix: ‘app’,
    suffix: ‘v1.0.2’,
    });

    // 更新时自动生效
    clientsClaim();

    // 预缓存文件,self.__WB_MANIFEST是workbox生成的文件地址数组,项目中打包生成的所有静态文件都会自动添加到里面
    precacheAndRoute(self.__WB_MANIFEST || []);

    // 单页应用需要应用NavigationRoute进行缓存,此处可自定义白名单和黑名单
    // 跳过登录和退出页面的拦截
    const handler = createHandlerBoundToURL(’/index.html’);
    const navigationRoute = new NavigationRoute(handler, {
    denylist: [
    /login/,
    /logout/,
    ],
    });
    registerRoute(navigationRoute);
    // 运行时缓存配置
    // 接口数据使用服务端数据
    registerRoute(/^api/,new NetworkOnly());

    //图片cdn地址,属于跨域资源,我们使用StaleWhileRevalidate缓存策略
    registerRoute(/^https://img.xxx.com//,new StaleWhileRevalidate());
    复制代码上述的代码有一段针对单页应用处理的逻辑,应为单页应用依靠路由变化来加载不同的内容,使用navigationRoute可以匹配导航请求,从而从换从中加载index.html,但默认情况会拦截所有导航请求,如果需要控制,可以在方法中添加白名单和黑名单加以控制。
    第四步:处理Service Worker的更新和离线状态
    更新状态
    配置完成后,我们需要注意service worker的更新和离线状态,service worker的更新较为复杂,如果处理不当回引发各种问题,目前主流的方式就是每次发版,提醒用户更新,如果用户点击确定更新,新发版的service worker会替换掉旧的service worker,此代码我们项目中放在了入口文件中(webpack配置的入口文件),示例代码如下:
    // sw.updated是在注册文件中添加的自定义事件
    window.addEventListener(‘sw.updated’, event => {
    const e = event;

    const reloadSW = async () => {
      // Check if there is sw whose state is waiting in ServiceWorkerRegistration
      // https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration
      const worker = e.detail && e.detail.waiting;
    
      if (!worker) {
        return true;
      } // Send skip-waiting event to waiting SW with MessageChannel
    
      await new Promise((resolve, reject) => {
        const channel = new MessageChannel();
    
        channel.port1.onmessage = msgEvent => {
          if (msgEvent.data.error) {
            reject(msgEvent.data.error);
          } else {
            resolve(msgEvent.data);
          }
        };
    
        worker.postMessage(
          {
            type: 'skip-waiting',
          },
          [channel.port2],
        );
      }); 
      // Refresh current page to use the updated HTML and other assets after SW has skiped waiting
    
      window.location.reload(true);
      return true;
    };
    
    const key = `open${Date.now()}`;
    const btn = (
      <Button
        type="primary"
        onClick={() => {
          notification.close(key);
          reloadSW();
        }}
      >
        确定
      </Button>
    );
    notification.open({
      message: "新版本发布",
      description: "Boss发布新版本了,请点击页面更新",
      btn,
      key,
      onClose: async () => {},
    });
    

    });
    复制代码对应sw.js文件里面要监听主线程传递过来的更新事件,代码如下:
    // service worker通过message和主线程通讯
    addEventListener(‘message’, event => {
    const replyPort = event.ports[0];
    const message = event.data;
    console.log(message)
    if (replyPort && message && message.type === ‘skip-waiting’) {
    event.waitUntil(
    self.skipWaiting().then(
    () =>
    replyPort.postMessage({
    error: null,
    }),
    error =>
    replyPort.postMessage({
    error,
    }),
    ),
    );
    }
    });
    复制代码离线状态
    对于离线状态的监听比较简单,在入口文件中添加以下代码即可:
    window.addEventListener(‘sw.offline’, () => {
    message.warning(“当前处于离线状态”,0);
    });
    复制代码检查效果
    经过上述四个步骤,我们就能将service worker引入到我们已有的用webpack构建的项目上。
    如果正常引入,我们可以在控制台中看到下图:

    展开全文
  • Workbox module gives errors

    2020-11-24 14:55:35
    at http://localhost:5000/_nuxt/workbox.dev.c21f51f2.js:169:1358 at Generator.throw () at step (http://localhost:5000/_nuxt/workbox.dev.c21f51f2.js:95:30) at ...
  • Updates for Workbox v6

    2020-12-31 22:28:50
    <p>Once Workbox v6 is released, we should update that URL to reflect the new way of using a <code>PrecacheStrategy</code> to handle requests for precached resources. See ...
  • <div><p>While digging in I found out that <code>workbox-webpack-plugin</code> limits all of the "transform" options to the <code>globPattern</code> files only. I.e. don't run <code>...
  • <p>Workbox's <code>debug</code> mode is currently tied to <code>WP_DEBUG</code>: <p>...
  • 4. Missing: importScripts('https://storage.googleapis.com/workbox-cdn/releases/5.1.2/workbox-sw.js'); 5. Give a no function workbox error. <p><strong>Expected behavior The workbox function ...
  • <div><p>Hi! 👋 <p>Thank you for this great package.... Please see https://goo.gl/EQ4Rhm for more info. </code></pre> <p>Thanks!</p><p>该提问来源于开源项目:hanford/next-offline</p></div>
  • <div><p>Bumps <a href="https://github.com/googlechrome/workbox">workbox-webpack-plugin</a> from 3.6.3 to 5.0.0. Release notes <p><em>Sourced from <a href="https://github.com/googlechrome/workbox/...
  • is there a way to exclude some url (specially third parties analytics tracking like parsely or comscore) from being processed by workbox? I have already tried to use the NetworkOnly strategy for these...
  • _iterator4 : _iterator4[Symbol.iterator]()' Duplicate declaration '_iterator = _isArray ? _iterator : _iterator[Symbol.iterator]()' Duplicate declaration '_iterator2 = _...
  • Fixes rejections in Windows tests (<a href="https://github-redirect.dependabot.com/googlechrome/workbox/issues/2452">#2452)</li><li><a href="https://github.com/GoogleChrome/workbox/commit/18939e4cbc...
  • Fixes rejections in Windows tests (<a href="https://github-redirect.dependabot.com/googlechrome/workbox/issues/2452">#2452)</li><li><a href="https://github.com/GoogleChrome/workbox/commit/18939e4cbc...

空空如也

空空如也

1 2 3 4 5 6
收藏数 106
精华内容 42
关键字:

workbox4