首页 > 解决方案 > 用 fetch 事件替换 service worker 中的资产

问题描述

我正在尝试与我的服务人员热切换主题样式表。

我为 Jekyll 创建了一个主题(与 GitHub Pages 一起使用),并希望能够在深色主题和浅色主题之间进行替换(“关灯”)并使其保持一致。

现在,这是通过将状态保存到localstorage并据此替换主题link元素来实现的。

问题是,在获取原始白色主题之后进行检查,因此即使localstorage设置了深色主题,用户也会看到白色主题的闪光

这是它目前的行为方式(按钮位于右上角): https ://thatkookooguy.github.io/postmortem-demo/postmortem/002

更改主题后,尝试导航到另一个页面。页面以白色主题加载,然后更改为深色主题

我想我可以通过在服务工作者中切换脚本来解决这个问题,因为我可以addEventListenerfetch事件并用另一个提取替换它在承诺链中。

但是代码似乎不起作用。如果我在事件回调中添加一个调试点,它永远不会触发(不是在资产上,也不是在实际页面获取上)

HTML头

<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <link rel='stylesheet prefetch' href='//fonts.googleapis.com/css?family=Fredoka+One|Righteous'>
  <!-- THIS SHOULD BE INTERCEPTED BY THE SERVICE WORKER -->
  <link id="bulma-theme" rel="stylesheet prefetch" href="//unpkg.com/bulmaswatch/default/bulmaswatch.min.css">
  <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
  <link rel="stylesheet" type="text/css" href="{{ site.baseurl }}/assets/style.css" />
  <script>
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker
        .register('{{ site.baseurl }}/assets/sw.js')
        .then(function() {
          console.log("Service Worker Registered");
        });
    }
  </script>
</head>

服务工作者代码

importScripts('localforage.min.js');

// remember the theme
this.onmessage = function(msg) {
  if (!msg.data.theme) {
    return;
  }

  localforage.setItem('theme', msg.data.theme);
}

// replace link fetching with correct theme
this.addEventListener('fetch', function(event) {
  console.log(event.request.url);

  if (event.request.url.contains('/bulmaswatch/')) {
    return localforage.getItem('theme')
      .then(function(theme) {
        if (theme === 'kb-dark-theme') {

          let darkThemeRequest = new Request(request.url.replace('bulmaswatch/default/', 'bulmaswatch/superhero/'), {
            method: request.method,
            headers: request.headers,
            mode: 'same-origin',
            credentials: request.credentials,
            redirect: 'manual'
          });

          return fetch(darkThemeRequest);
        }

        return fetch(event.request);
      })
  }
});

// Log SW installation
this.addEventListener('install', function(event) {
  console.log('[SW]: installing....');
});

// Log SW activation
this.addEventListener('activate', function activator(event) {
  console.log('[SW]: activate!');
});

在 chrome 中调试时(和 chrome canary,因为我听说那里有更好的 service worker 调试支持),fetch甚至从未调用过回调。没有记录任何请求。

我确实看到服务人员在 chrome 的开发工具中注册并处于活动状态。我看到控制台中记录了其他事件。

此外,当调试并在脚本开头放置断点时,看起来好像bulmaswatch.min.css已经获取了。

知道我错过了什么吗?:-)

谢谢

标签: javascripthtmlservice-workerinterceptorservice-worker-events

解决方案


根本问题与您的服务人员的范围有关。

当我访问https://thatkookooguy.github.io/postmortem-demo/postmortem/002并查看 Chrome 的 DevTools 中的 Application > Service Workers 面板时,我看到 service worker 有一个范围https://thatkookooguy.github.io/postmortem-demo/assets并且不控制任何客户端。

应用程序面板截图

如果您将sw.js文件移出/assets/子目录,并移至站点的顶级目录(即/postmortem-demo/),然后切换您的注册码以反映这一点,那么您的服务工作者应该能够控制您的客户端页面:

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/postmortem-demo/sw.js');
}

(这意味着在本地开发时,您还应该使用 URL 前缀为您的 Web 应用程序提供/postmortem-demo/匹配的 URL。)

以下资源包含有关 Service Worker 范围的更多信息:


推荐阅读