离线策略加入 Service Worker 食谱

serviceworke.rs 是一个包含常见和 不常见 Service Worker 用例的汇编,包括推送示例、使用模式、性能技巧和缓存策略。

Service Worker 食谱中的菜谱以游乐场或实验室的形式呈现,包含功能完备的客户端-服务器设置,您可以在其中使用浏览器内开发者工具学习和实验结果。

然而,食谱远非全面,我们意识到它缺少一些基本材料和用户反馈机制。今天,我很自豪地宣布 Service Worker 食谱的一些变化,首先是关于 **缓存策略** 的新部分。

缓存策略

缓存策略包含演示几种从服务工作者提供内容的方法的菜谱。这些菜谱遵循相同的布局,其中两个 iframe 并排显示。两者都显示一个指向同一在线图片的图像元素。

第一个 iframe 不受服务工作者拦截,因此图片始终显示来自服务器的最新内容。相反,第二个 iframe 受服务工作者控制,内容根据实施的缓存策略提供。

Layout for offline recipes: two iframes, the first controlled and the second not.

图片内容每 10 秒在服务器上更改,您有一个按钮可以同时刷新两个 iframe 并比较图片的变化。

cache-update-refresh-out-of-sync

一些缓存策略来自 Jake Archibald 的《离线食谱》 中的一篇鼓舞人心的文章,其他则是我们自己开发的。

仅缓存

最基本的示例:使用 *仅缓存*,请求永远不会到达网络。相反,它们将由服务工作者从本地缓存中提供。

self.addEventListener('fetch', function(evt) {
  evt.respondWith(fromCache(evt.request));
});

function fromCache(request) {
  return caches.open(CACHE).then(function (cache) {
    return cache.match(request).then(function (matching) {
      return matching || Promise.reject('no-match');
    });
  });
}

在此实施中,仅缓存的资产在安装服务工作者时存储,并将一直保留在那里,直到安装了新版本的 worker。

self.addEventListener('install', function(evt) {
  evt.waitUntil(precache());
});

function precache() {
  return caches.open(CACHE).then(function (cache) {
    return cache.addAll([
      './controlled.html',
      './asset'
    ]);
  });
}

您可以将 **仅缓存策略** 用于您网站的 UI 相关资产,例如图像、HTML、精灵图或 CSS 文件。

缓存和更新

仅缓存策略的这种细微变化也从本地缓存中提供资产,但它也发送网络请求以获取资产的更新版本。新内容然后替换本地缓存中的旧资产。

self.addEventListener('fetch', function(evt) {
  evt.respondWith(fromCache(evt.request));
  evt.waitUntil(update(evt.request));
});

function update(request) {
  return caches.open(CACHE).then(function (cache) {
    return fetch(request).then(function (response) {
      return cache.put(request, response);
    });
  });
}

使用这种 **缓存和更新策略**,您的资产将不再与在线资产同步,但它们将在第二次请求(大致相当于第二次访问)时同步。

在传递独立的、非关键内容(例如头像或图标)时,使用此策略完全没问题。避免依赖于此策略传递依赖资产(例如完整的 UI 主题),因为没有保证资产会按需同时更新。

缓存、更新和刷新

对先前策略的又一次调整,现在添加了 **刷新成分**。

使用 **缓存、更新和刷新**,客户端将在新内容可用时收到服务工作者的通知。这样,您的网站就可以显示内容,而无需等待网络响应,同时为 UI 提供了一种以受控方式显示最新内容的方法。

self.addEventListener('fetch', function(evt) {
  evt.respondWith(fromCache(evt.request));
  evt.waitUntil(
    update(evt.request)
    .then(refresh)
  );
});

function refresh(response) {
  return self.clients.matchAll().then(function (clients) {
    clients.forEach(function (client) {
      var message = {
        type: 'refresh',
        url: response.url,
        eTag: response.headers.get('ETag')
      };
      client.postMessage(JSON.stringify(message));
    });
  });
}

这在获取任何类型的内容时特别有用。这与之前的策略不同,因为它不需要用户刷新或第二次访问网站。因为客户端知道新内容,所以 UI 可以在智能、非侵入性的方式中更新。

嵌入式回退

在某些情况下,您始终希望始终显示某些内容来替换由于任何原因(网络错误、404、无连接)而缺失的内容。通过将 **该内容嵌入到服务工作者** 中,可以确保始终可用离线内容。

self.addEventListener('fetch', function(evt) {
  evt.respondWith(networkOrCache(evt.request).catch(function () {
    return useFallback();
  }));
});

// Dunno why this is shown as the actual SVG in WordPress but it looks awesome!
// You can see the source code in the recipe.
var FALLBACK =
    '' +
    '  ' +
    '  ' +
    '  ' +
    '  ' +
    '';

function useFallback() {
  return Promise.resolve(new Response(FALLBACK, { headers: {
    'Content-Type': 'image/svg+xml'
  }}));
}

 

在此菜谱中,作为缺失内容替换的 SVG 包含在 worker 中。一旦安装,回退将在不执行新的网络请求的情况下可用。

网络或缓存

Service Workers 将自己置于客户端和互联网之间。在某种程度上,它们允许开发者模拟他们理想的网络行为。此策略通过对网络响应施加时间限制来利用/增强这种想法。

self.addEventListener('fetch', function(evt) {
  evt.respondWith(fromNetwork(evt.request, 400).catch(function () {
    return fromCache(evt.request);
  }));
});

function fromNetwork(request, timeout) {
  return new Promise(function (fulfill, reject) {
    var timeoutId = setTimeout(reject, timeout);
    fetch(request).then(function (response) {
      clearTimeout(timeoutId);
      fulfill(response);
    }, reject);
  });
}

 

使用此菜谱,请求由服务工作者拦截并传递到网络。如果响应花费的时间过长,则会中断该过程,并从本地缓存中提供内容。

**有时间限制的网络或缓存** 实际上可以与任何其他技术结合使用。该策略只是让网络有机会快速使用最新内容进行响应。

用户反馈

我们想知道这些菜谱是否有用,以及您是否觉得它们清晰或令人困惑。它们是否提供了独特的价值,或者它们是多余的?我们在菜谱中添加了 Disqus 评论,以便您分享您的反馈。使用 Facebook、Twitter、Google 或 Disqus 登录,并告诉我们此菜谱是如何为您服务的,或者参与有关推荐用例的讨论。

以及更多即将推出的内容

我们不会止步于此。更多菜谱即将推出,新增强功能也即将推出:改进的请求菜谱方法、更简单的贡献流程、视觉更新和更新后的菜谱布局是我们正在考虑的事情。如果您喜欢 **serviceworke.rs**,请与您的朋友和同事分享。随意在您的演讲或演示文稿中使用这些菜谱,最重要的是,通过在网站上发表评论、提交 GitHub 问题或 直接发推给我 来帮助我们 😉

您的意见真的非常感谢!

关于 Salva

Mozilla 的前端开发者。开放网络和 WebVR 倡导者,我喜欢编程语言、电影、音乐、电子游戏和啤酒。

更多 Salva 的文章...


4 条评论

  1. jxn

    这些示例非常有用。我很想看到一个允许用户手动升级过期服务工作者的示例。我还想看到离线使用页面预取和一次性后台同步的示例。

    2016 年 10 月 19 日 下午 10:48

    1. Salva

      我已经为手动升级 SW [1] 和后台同步 [2] 提交了一些错误,但我没有理解你所说的页面预取是什么意思。你能详细说明一下吗?

      [1] https://github.com/mozilla/serviceworker-cookbook/issues/256
      [2] https://github.com/mozilla/serviceworker-cookbook/issues/257

      2016 年 10 月 20 日 下午 12:40

      1. jxn

        是的,我正在寻找像 Chrome 演示文稿中的这个演示这样的示例:https://googlechrome.github.io/samples/service-worker/prefetch/index.html

        在这种情况下,内容是在用户明确请求之前获取的。

        2016 年 10 月 27 日 上午 7:12

        1. jxn

          我已经添加了一个问题来创建这样的菜谱:https://github.com/mozilla/serviceworker-cookbook/issues/260

          2016 年 10 月 27 日 上午 7:23

本文的评论已关闭。