如何使用 Service Worker 让 Jekyll 博客离线工作

已发表: 2017-01-26

Short Bytes:你知道吗,随着 Service Worker 的出现,人们可以开始让网站离线工作! 此类 Web 应用程序称为 PWA(渐进式 Web 应用程序)。 在这个方法中,我将帮助你使用 service worker 来使基于 Jekyll 的博客/网站离线工作以及一些很酷的东西。

(注意文章中的代码片段取自我博客的代码库。如果需要,可以参考。如果您是 Jekyll 新手,可以阅读 CSS Tricks 3 系列文章。)

背景

之前有一个基于 yuk YAML 文件的 App Cache,它本质上是非常硬编码的,不能用于动态缓存资产和网页。 输入,服务人员。 基于简单纯事件的 Javascript API 进行动态服务工作者缓存以存储资产,以便在没有网络时可以使用它们来服务网页。

服务人员于 2014 年登陆 Chrome Canary,但其规范仍在修改/添加和设计中。 2015 年和 2016 年,谷歌的 Chrome 团队在浏览器中大力宣传这项新技术。 只有 Apple 没有在他们的设备上支持这一点(即使在撰写本文时)(出于未知原因)[他们也没有积极参与任何关于 service worker 的规范讨论]。

什么是服务人员? 基本上,它是一个使用类固醇的网络工作者。 Web Worker 的一个特点是,Web Worker 的所有任务都与主 JavaScript 执行线程(事件循环)分开(异步)运行。 此功能有助于运行 CPU 或内存密集型任务,例如复杂的计算,而不会影响 Web 应用程序用户界面的性能。

Service Worker 让我们可以在浏览器的 Service Worker 缓存中缓存(存储很长时间)资产,例如 JavaScript、CSS、HTML、图像、字体文件,因此下次用户加载该页面时,它几乎会立即加载. 而且由于在这种缓存策略中,浏览器首先从 service worker 缓存中查找资产的可用性,即使网页处于离线状态,也会提供网页! 如果缓存中没有任何资产,则会发送网络请求以获取它。

Service Worker 还启用了当今许多网站上常见的推送通知,包括 Facebook、Web 上的 Whatsapp 和 Twitter。 我们将主要讨论离线功能。 这个方法是特定于 Jekyll 的,但是,大多数 service worker 代码通常可以应用于任何网站。

由于 Jekyll 提供静态内容(它是一个静态站点生成器,呵呵! ),我们的 service worker 代码将非常基本且易于理解。

来吧:

在所有相关页面上,都会执行以下脚本。 它正在做以下事情:

  1. 检查浏览器中是否存在 service worker API 并注册 service worker。
  2. 当 service worker 被激活后,向用户发送一条漂亮的 toast/chip 消息,表明该网站现在可以离线使用了。
 功能 showOfflineToast() {
  let offlineToast = document.querySelector('.offline-ready');
  offlineToast.classList.add('active');
  设置超时(函数(){ 
    offlineToast.className = offlineToast.className.replace("active", "").trim();
  }, 5500);
}

// (1)
if (navigator.serviceWorker) {
  navigator.serviceWorker.register('/sw.js').then(function(reg) {
      如果(!reg.installing)返回;
      console.log("[*] ServiceWorker 正在安装...");

      var worker = reg.installing;

      worker.addEventListener('statechange', function() {
          if (worker.state == '冗余') {
              console.log('[*] 安装失败');
          }
          if (worker.state == '已安装') {
              console.log('[*] 安装成功!');
          }
          // (2)
          if (worker.state == '激活' && !navigator.serviceWorker.controller) {
            showOfflineToast();
          }
      });
  });
} 

gdad-s-river.github.io-(iPhone 6)

您可以将这段代码添加到 Jekyll 代码的include目录中的serviceWorker.html文件中,并使用 Jekyll 的液体模板引擎将其包含在default.html

<!DOCTYPE html>
<html>

  {% 包括 head.html %}

  <正文>
    {% 包含 header.html %}

    <div class="页面内容">
      <div class="包装器">
        {{ 内容 }}
      </div>
    </div>

    {% 包括 footer.html %}

    <!-- 在脚本标签中包含上述代码-->
    {% 包括 serviceWorker.html %} 

    <div class="offline-ready">网站已准备好离线使用</div>
  </正文>

</html>

现在到真正发挥作用的服务工作者代码。 此代码位于 Jekyll 代码根目录下的sw.js中。

 //sw.js
---
布局:空
---

const staticCacheName = "gdad-s-river-static-v61";

console.log("正在安装 service worker");

常量文件缓存 = [
  "/",
  {% for page in site.html_pages %}
    '{{ page.url }}',
  {% endfor %}
  {% for post in site.posts %}
    '{{ post.url }}',
  {% endfor %}

  // 可以自动输入而不是手动输入
  "/assets/images/bhavri-github-callbacks.png",
  "/assets/images/bhavri-github-issues.png",
  "/assets/images/jakethecake-svg-line-anime.png",
  "/assets/images/svg-animated-mast-text-shapes-tweet.png",
  "css/main.css",
  “/关于/”,
  “/index.html”
];

staticCacheName是缓存版本,每次我对缓存的响应进行一些更改时都会更新它(例如,我在 CSS 文件或博客文章中进行更改)。 而且,我只是定义了我想要拦截和缓存在数组中的请求(在下一个片段中使用)。

 //sw.js

self.addEventListener("安装", function(e){
  self.skipWaiting();
  e.等待直到(
    caches.open(staticCacheName).then(function(cache){
      返回 cache.addAll(filesToCache);
    })
  )
});

self.skipWaiting就是说,每次这个sw.js文件更改时,新版本的 service worker 不应该排队,而是立即激活(可以要求用户提示刷新页面,给出如下消息网页已更新/更改,单击刷新以加载新帖子或其他任何内容。 ),丢弃旧版本。

java-and-android-square-ad-1

e.waitUntil引用 MDN 网站:

ExtendedEvent.waitUntil()方法延长了事件的生命周期。 在服务工作者中,延长事件的生命周期可以防止浏览器在事件中的异步操作完成之前终止服务工作者。”

我打开一个名为gdad-s-river-static-v61 的缓存,它返回一个带有我的缓存名称的承诺,然后我调用cache.addAll (它在后台使用 fetch API),它获取所有请求数组提供并缓存它们。

进入下一个片段!

 //sw.js

self.addEventListener("激活", function(e){
  e.等待直到(
    caches.keys().then(function(cacheNames){
      返回 Promise.all(
        缓存名称.过滤器(功能(缓存名称){
          return cacheName.startsWith("gdad-s-river-static-")
            && 缓存名称 != 静态缓存名称;
        }).map(函数(cacheName){
          返回缓存。删除(缓存名称);
        })
      )ß
    })
  )
});

当服务工作者激活时,我确保所有不是最新版本的服务工作者都会被删除。 例如,如果我的最新缓存版本是gdad-s-river-static-v61 ,并且有人仍在gdad-s-river-static-v58上,那么在他/她下次访问时,我希望该客户不关心一次抽一个版本,但直接删除该版本以安装最新版本。

 //sw.js

self.addEventListener("fetch", function(e){
  e.respondWith(
     caches.match(e.request).then(function(response) {
       返回响应 || 获取(e.request);
     })
   )
});

在 fetch 事件中,我只是告诉服务人员如何响应发出的特定请求(因为我们正在劫持响应赋予权力,所以服务人员只能在安全的又名 https 原始网站上工作)。 我告诉它将请求与浏览器中缓存的请求相匹配,如果它没有找到对请求的特定响应,则通过网络获取它,否则从缓存中提供它。

多田! Service Worker 使 Jekyll 驱动的博客离线!

惊喜! 很酷的事情:

Bummer:这在 iOS 设备上不起作用。

如果您在项目的根目录中添加一个 Web 应用manifest.json文件,如下所示:

 {
  “名称”:“gdad-s-河”,
  "short_name": "gdad-s-river",
  "theme_color": "#2196f3",
  "background_color": "#2196f3",
  “显示”:“独立”,
  “范围”: ”/”,
  "start_url": "/",
  “图标”:[
    {
     "src": "assets/images/favicon_images/android-icon-36x36.png",
     “尺寸”:“36x36”,
     "type": "图片\/png",
     “密度”:“0.75”
    },
    {
     "src": "assets/images/favicon_images/android-icon-48x48.png",
     “尺寸”:“48x48”,
     "type": "图片\/png",
     “密度”:“1.0”
    },
    {
     "src": "assets/images/favicon_images/android-icon-72x72.png",
     “尺寸”:“72x72”,
     "type": "图片\/png",
     “密度”:“1.5”
    },
    {
     "src": "assets/images/favicon_images/android-icon-96x96.png",
     “尺寸”:“96x96”,
     "type": "图片\/png",
     “密度”:“2.0”
    },
    {
     "src": "assets/images/favicon_images/android-icon-144x144.png",
     “尺寸”:“144x144”,
     "type": "图片\/png",
     “密度”:“3.0”
    },
    {
     "src": "assets/images/favicon_images/android-icon-192x192.png",
     “尺寸”:“192x192”,
     "type": "图片\/png",
     “密度”:“4.0”
    }
  ]
}

并将其添加到 head 标签内的head.html文件中,

 <头部>
 <!-- 一些东西 -->
	<link rel="manifest" href="/manifest.json">
 <!-- 一些东西 -->
</head>

然后,在您第二次访问您的网站时(5 分钟内),将提示用户将您的网站添加到您的主屏幕(以图标形式驻留,就像其他本机应用程序一样),您将参与其中一个应用程序。

截图_20170123-232947

资源

  • Service Worker 的离线功能和缓存策略的详细信息可以在这本很棒的 Jake Archibald 的离线食谱中找到。
  • 一个非常详细的免费 Udacity 课程,介绍您需要了解的有关服务人员和 IndexDB 的所有信息。

你觉得 Jekyll 博客上的这篇文章有趣且有用吗? 不要忘记分享您的观点和反馈。

另请阅读:Ecosia——种植树木的搜索引擎