如何为单页面应用程序设置和配置 Service Worker 以显示离线页面

问题描述 投票:0回答:1

我有一个单页应用程序,正在注册服务人员。注册、安装和激活工作正常。

Fetch 没有。它永远不会着火。我猜测这是因为 SPA 使用子域 (api.mydomain.com) 来进行 API 调用,并且服务工作线程注册为 app.mydomain.com。

所以...

  1. 是否可以让 fetch 为 SPA 工作?
  2. 这是个好主意吗?
  3. 我们应该如何展示 SPA 的离线页面?

Service Worker 注册

<script>
      // ServiceWorker is a progressive technology. Ignore unsupported browsers
      if ("serviceWorker" in navigator) {
        navigator.serviceWorker
          .register("./src/sw.js")
          .then(function (registration) {
            console.log("Yay, service worker registered", registration);
          })
          .catch(function (err) {
            console.log("Noooo, sevice worker didnt register", err);
          });
      }
    </script>

服务人员档案

var precacheVersion = 22;
var precacheName = "precache-v" + precacheVersion;
var precacheFiles = ["/assets/offline.html"];

self.addEventListener("install", function (e) {
  console.log("[ServiceWorker] Installed");
  self.skipWaiting();

  e.waitUntil(
    caches.open(precacheName).then(function (cache) {
      console.log("[ServiceWorker] Precaching files");
      return cache.addAll(precacheFiles);
    })
  ); // end e.waitUntil
});

self.addEventListener("activate", function (e) {
  console.log("[ServiceWorker] Activated");

  e.waitUntil(
    caches.keys().then(function (cacheNames) {
      return Promise.all(
        cacheNames.map(function (thisCacheName) {
          if (
            thisCacheName.indexOf("precache") !== -1 &&
            thisCacheName !== precacheName
          ) {
            console.log(
              "[ServiceWorker] Removing cached files from old cache - ",
              thisCacheName
            );
            return caches.delete(thisCacheName);
          }
        })
      );
    })
  ); // end e.waitUntil
});
self.addEventListener("fetch", function (e) {
  console.log("[ServiceWorker] Fetch event for ");
  e.respondWith(
    caches.match(e.request).then(function (cacheResponse) {
      if (cacheResponse) {
        console.log("Found in cache!");
        return cacheResponse;
      }
      return fetch(e.request)
        .then(function (fetchResponse) {
          return fetchResponse;
        })
        .catch(function (err) {
          var isHTMLPage =
            e.request.method == "GET" &&
            e.request.headers.get("accept").includes("text/html");
          if (isHTMLPage) {
            return caches.match("/assets/offline.html");
          }
        });
    })
  );
});
progressive-web-apps single-page-application service-worker
1个回答
0
投票

您致电

navigator.serviceWorker.register("./src/sw.js")
。这意味着您的整个站点现在由您的服务人员处理,对吗?

可悲的是,事情没那么简单。如果您调用

register()
时未在可选
options
参数中指定范围,则 Service Worker 的位置将成为其范围。根据MDN,

默认情况下,Service Worker 注册的

scope
值设置为 Service Worker 脚本所在的目录(通过解析
./
scriptURL
)。

那么“范围”是什么意思?

代表 URL 的字符串,定义 Service Worker 的注册范围;也就是说,Service Worker 可以控制的 URL 范围。

因此,如果您的脚本托管在

app.mydomain.com
上并位于相对 URL
src/sw.js
,则意味着它只能处理
app.mydomain.com/src
下的请求,这可能不是您想要的。我只能假设您的
fetch()
调用是直接对
app.mydomain.com
进行的,这超出了您的 Service Worker 的范围。

那么,如何解决这个问题呢?

  1. 将您的
    sw.js
    文件直接托管在服务器的根目录下,并使用
    navigator.serviceWorker.register("./sw.js")
    注册您的 Service Worker。当然,这是一个简单的修复,但您可能希望所有脚本文件都位于
    src/
    目录下以保持一致性。
  2. 使用 scope 属性添加额外的选项参数,调用
    register
    ,如下所示:
    navigator.serviceWorker.register("./src/sw.js", {
        scope: "app.domain.com" // could be a relative URL
    })
    
    这样,范围就明确设置为站点的根目录,现在每个获取请求都应该通过您的服务工作线程。
© www.soinside.com 2019 - 2024. All rights reserved.