[译] Service workers:Progressive Web Apps 背后的小英雄

Service workers:Progressive Web Apps 背后的小英雄

Service workers 是 Progressive Web Apps 的核心。它们容许缓存资源和推送通知,这是原生 app 应用的两个突出特性。html

service worker 是你的网页和网络之间的 可编程代理,它能够拦截和缓存网络请求。这实际上可让你 使本身的 app 具备离线优先的体验前端

Service workers 是一种特殊的 web worker:一个关联工做环境上运行的网页且与主线程分离的 JavaScript 文件。它带来了非阻塞这一优势 —— 因此计算处理能够在不牺牲 UI 响应的状况下完成。android

由于它在单独的线程上,所以它没有访问 DOM 的权限,也没有访问本地存储 APIs 和 XHR API 的权限。它只能使用 Channel Messaging API 与主线程通讯。ios

Service Workers 与其余新进的 Web APIs 搭配:git

  • Promises
  • Fetch API
  • Cache API

它们 只在使用 HTTPS 协议的页面可用(除了本地请求不须要安全链接,这会使测试更简单。)。github

后台运行

Service workers 独立运行,当与其相关联的应用没有运行的时候也能够接收消息。web

它们能够后台运行的几种状况:编程

  • 当你的手机应用 在后台运行,没有激活
  • 当你的手机应用 关闭 甚至没有在后台运行
  • 浏览器关闭,若是 app 运行在浏览器上

service workers 很是有用的几种场景:后端

  • 它们能够做为缓存层来处理网络请求,而且缓存离线时要使用的内容
  • 它们容许推送通知

service worker 只有在须要的时候运行,否则则中止运行。promise

离线支持

传统上,web app 的离线体验一直不好。没有网络,web app 一般根本没法工做。另外一方面,原生手机 app 则有能力提供一种能够离线运行的版本或者友好的消息提示。

这就不是一种友好的消息提示,但这是 Chrome 中一个网页在没有网络链接状况下的样子:

可能惟一的好处就是你能够点击恐龙来玩免费的小游戏 —— 但这很快就会变的无聊。

最近,HTML5 AppCache 已经承诺容许 web apps 缓存资源和离线工做。可是它缺少灵活性,并且混乱的表现也让它不足胜任这项工做(并已经中止)。

Service workers 是新的离线缓存标准。

能够进行哪一种缓存?

在安装期间预缓存资源

能够在第一次打开 app 的时候安装在整个应用中重用的资源,如图片,CSS,JavaScript 文件。

这就给出了所谓的 App Shell 体系

缓存网络请求

使用 Fetch API,咱们能够编辑来自服务器的响应,若是服务器没法访问,能够从缓存中提供响应做为替代。

Service Worker 生命周期

service worker 通过如下三个步骤才能提供完整的功能:

  • 注册
  • 安装
  • 激活

注册

注册告诉浏览器 service worker 在哪里,并在后台开始安装。

注册放置在 worker.js 中 service worker 的示例代码:

if ('serviceWorker' in navigator) { 
  window.addEventListener('load', () => {   
    navigator.serviceWorker.register('/worker.js') 
    .then((registration) => { 
      console.log('Service Worker registration completed with scope: ', registration.scope) 
    }, (err) => { 
      console.log('Service Worker registration failed', err)
    })
  })
} else { 
  console.log('Service Workers not supported') 
}
复制代码

即便此代码被屡次调用,若是 service worker 是新的,而且之前没有被注册,或者已更新,浏览器将仅执行注册。

做用域

register() 调用还接受一个做用域参数,该参数是一个路径用来肯定应用程序的哪一部分能够由 service worker 控制。

它默认包含 service worker 的文件夹中的全部文件和子文件夹,因此若是将它放到根文件夹,它将控制整个 app。在子文件夹中,它将只会控制当前路径下的页面。

下面的示例经过指定 /notifications/ 文件夹范围来注册 service worker。

navigator.serviceWorker.register('/worker.js', { 
  scope: '/notifications/' 
})
复制代码

/ 很重要:在这种状况下,页面 /notifications 不会触发 service worker,而若是做用域是:

{ scope: '/notifications' }
复制代码

它就会起做用。

注意:service worker 不能从一个文件夹中“提高”本身的做用域:若是它的文件放在 /notifications 下,它不能控制 / 路径或其余不在 /notifications 下的路径。

安装

若是浏览器肯定 service worker 过时或者之前从未注册过,则会继续安装。

self.addEventListener('install', (event) => { 
  //... 
});
复制代码

这是使用 service worker 初始化缓存的好时机。而后使用 Cache API 缓存 App Shell 和静态资源。

激活

一旦 service worker 被成功注册和安装,第三步就是激活。

这时,当界面加载时,service worker 就能正常工做了。

它不能和已经加载的页面进行交互,所以 service worker 只有在用户和应用交互的第二次或从新加载已打开的页面时才有用。

self.addEventListener('activate', (event) => { 
  //... 
});
复制代码

这个事件的一个好的用例是清除旧缓存和一些关联到旧版本而且没有被新版本的 service worker 使用的文件。

更新 Service Worker

要更新 service worker,你只需修改其中的一个字节。当寄存器代码运行的时候,它就会被更新。

一旦更新了 service worker,直到全部关联到旧版本 service worker 已加载的页面所有关闭,新的 service worker 才会起做用。

这确保了在已经工做的应用/页面上不会有任何中断。

刷新页面还不够,由于旧的 worker 仍在运行,且没有被删除。

Fetch 事件

当网络请求资源时 fetch 事件 被触发。

这给咱们提供了在发起网络请求前查看缓存的能力。

例如,下面的代码片断使用 Cache API 来检查请求的 URL 是否已经存储在缓存响应里面。若是已存在,它会返回缓存中的响应。不然,它会执行 fetch 请求并返回结果。

self.addEventListener('fetch', (event) => {
  event.respondWith( 
    caches.match(event.request) 
      .then((response) => { 
        if (response) { 
          //entry found in cache 
          return response 
        } 
        return fetch(event.request) 
      } 
    ) 
  ) 
})
复制代码

后台同步

后台同步容许发出的链接延迟,直到用户有可用的网络链接。

这是确保用户能离线使用 app,能对其进行操做,而且当网络链接时排队进行服务端更新(而不是显示尝试获取信号的无限旋转圈)的关键。

navigator.serviceWorker.ready.then((swRegistration) => { 
  return swRegistration.sync.register('event1') 
});
复制代码

这段代码监听 service worker 中的事件:

self.addEventListener('sync', (event) => { 
  if (event.tag == 'event1') { 
    event.waitUntil(doSomething()) 
  } 
})
复制代码

doSomething() 返回一个 promise 对象。若是失败,另外一个同步事件将安排自动重试,直到成功。

这也容许应用程序在有可用网络链接时,当即从服务器更新数据。

推送事件

Service workers 让 web apps 为用户提供本地推送。

推送和通知其实是两种不一样的概念和技术,它们结合起来就是咱们所知的 推送通知。推送提供了容许服务器向 service worker 发送消息的机制,通知就是 servic worker 向用户显示信息的方式。

由于 service workers 即便在 app 没有运行的时候也能够运行,它们能够监听即将到来的推送事件。而后它们要么提供用户通知,要么更新 app 状态。

推送事件用后端经过浏览器推送服务启动,如 Firebase 提供的推送服务。

下面这个例子展现了 web worker 如何可以监听到即将到来的推送事件:

self.addEventListener('push', (event) => { 
  console.log('Received a push event', event) 
  const options = { 
    title: 'I got a message for you!', 
    body: 'Here is the body of the message', 
    icon: '/img/icon-192x192.png', 
    tag: 'tag-for-this-notification', 
  } 
  event.waitUntil( 
    self.registration.showNotification(title, options) 
  ) 
})
复制代码

有关控制台日志的说明:

若是 service work 有任何控制台日志语句(console.log 和其相似),请确保你打开了 Chrome Devtools(或相似工具)提供的 Preserve log 功能。

不然,因为 service worker 在页面加载前执行,而且在加载页面前清除了控制台,你将不会在控制台看到任何日志输出。

感谢阅读这篇文章,关于这个主题还有不少值得学习的地方!我在关于前端开发的博客中发表了不少相关的内容,别忘记去看!😀

最初发表于flaviocopes.com


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索