使用Service Workers使您的应用程序脱机工作

null

服务工作者可用于改善您的站点和Web应用程序的加载时间和离线支持。在本教程中,我们将向您展示如何使用Service Worker逐步增强Web应用程序。首先,我们将介绍什么是服务工作者及其生命周期如何工作,然后我们将向您展示如何使用它来加速您的网站(此页面)并提供离线内容(第2页)。

然后我们会告诉你如何如何构建应用程序与服务人员。您将学习如何设置一个可以缓存和提供静态资产的裸机工作者(在后续负载上提供巨大的性能提升),然后如何缓存动态API响应并为我们的演示应用程序提供完全脱机支持。首先,让我们看看Service Workers究竟是什么,以及它们如何运作。

什么是服务工作者?

什么是服务工作者?这是一个用JavaScript编写的脚本,您的浏览器在后台运行。它不会影响主线程(JavaScript通常在网页上运行),并且不会与您的应用代码冲突或影响运行时性能。

服务工作者无法直接访问DOM或网页本身发生的事件和用户交互。可以将其视为位于网页和网络之间的层,允许它拦截和操纵页面发出的网络请求(例如Ajax请求)。这使其成为管理缓存和支持脱机使用的理想选择。

服务工作者生命周期

服务工作者的生活遵循一个简单的流程,但是当您习惯使用JS脚本立即工作时,它可能会有点混乱:

安装>等待(已安装)>激活>已激活>冗余

首次加载页面时,我们添加了注册码的index.html开始安装Service Worker。当没有现有Worker时,安装后将立即激活新的Service Worker。一个网页一次只能激活一个服务工作者。

如果已安装Worker,则将安装新的Service Worker,然后等待等待页面完全关闭然后重新加载。仅仅刷新是不够的,因为您可能打开了其他选项卡。您需要确保关闭页面的所有实例,否则新的Worker将不会激活。您不必关闭选项卡,只需导航到另一个站点并返回即可。

安装启用每个工人只会发生一次事件。一旦激活,服务工作者就可以控制页面,并可以开始处理事件,例如获取操作请求。

如果浏览器检测到工作文件本身已更新或安装或激活失败,则Service Worker将变为冗余。浏览器将查找字节差异以确定是否已更新工作程序脚本。

重要的是要注意,您永远不应更改(或修改)您的服务工作者的名称。您也不应该将工作文件本身缓存在服务器上,因为您无法轻松更新它,尽管浏览器现在足够聪明,可以忽略缓存标头。

01.克隆演示应用程序

好的,让我们开始学习如何在Service Workers的帮助下构建一个Web应用程序。对于本教程,您将需要在计算机上安装最新版本的Node.js和npm。

我们已经敲了一个演示应用程序,我们将用它作为本教程的基础(在这里克隆演示应用程序)。该应用程序是一个有趣的小项目,根据用户的位置获取五天天气预报。然后它将检查在一天结束之前是否预测到降雨并相应地更新UI。

它使用大型不必要的库(例如jQuery和Bootstrap)低效(有意)地构建,使用大的未经优化的图像来演示使用Service Worker时的性能差异。它目前的重量是一个荒谬的4.1MB。

02.获取API密钥

要从API获取天气数据,您需要从中获取免费的API密钥OpenWeatherMap

一旦你拿到钥匙,就打开吧的index.html并寻找Window.API_KEY变量在。将您的密钥粘贴到值中:

window.API_KEY ='paste-your-key-here';

03.启动开发服务器

现在我们准备开始研究这个项目了。首先,让我们运行以下命令来安装依赖项:

Npm安装

构建工具有两个任务。跑Npm开始在端口3000上启动开发服务器。运行Npm Run Build准备'生产'版本。请记住,这只是一个演示,因此不是真正的生产版本 - 没有缩小或任何东西 - 文件只是'加速'。

算法用于从文件的内容创建散列,例如9c616053e5。该算法将始终为相同的内容输出相同的散列,这意味着只要您不修改该文件,散列就不会改变。然后将散列附加到文件名,例如styles.css可能变为styles-9c616053e5.css。哈希表示文件的修订版 - 因此“加速”。

您可以安全地在服务器上缓存文件的每个修订版,而无需使缓存失效,这很昂贵,或者担心其他第三方缓存会提供错误的版本。

04.介绍您的服务工作者

现在让我们开始使用我们的服务工作者。在。的根目录中创建一个名为sw.js的文件SRC目录。然后添加这两个事件监听器来记录安装启用事件:

self.addEventListener('install',(event)=> {console.log(event);}); self.addEventListener('activate',(event)=> {console.log(event);});

这里的变量表示Service Worker的全局只读范围。这有点像窗口网页中的对象。

接下来,我们需要更新index.html文件并添加命令以安装Service Worker。在结束之前添加此脚本标签。它将注册我们的工作人员并记录其当前状态。

通过运行启动开发服务器Npm开始并在现代浏览器中打开页面。我们建议使用谷歌浏览器,因为它在其DevTools中具有良好的服务工作者支持,我们将在本教程中引用它们。您应该会在控制台中看到三件事;两个来自服务工作者安装启用事件,另一个将是注册的消息。

05.激活工人

我们要告诉我们的工人跳过等待步骤并立即激活。打开sw.js文件,将该行添加到安装事件监听器:

Self.skipWaiting();

现在,当我们更新Worker脚本时,它将在安装后立即控制页面。值得注意的是,这可能意味着新工作人员将控制可能由您的工作者的先前版本加载的页面 - 如果这将导致问题,请不要在您的应用程序中使用此选项。

您可以通过离开页面然后返回来确认这一点。你应该看到了安装启用安装新Worker时,事件再次触发。

Chrome DevTools有一个有用的选项,这意味着您可以通过重新加载来更新您的工作者。打开DevTools并转到Application选项卡,然后从左侧列中选择Service Worker。在面板的顶部是一个标记为重新加载时更新的复选框,勾选它。现在将在刷新时安装并激活您更新的工作程序。

06.确认更改

让我们通过添加确认这一点的console.log( '富')调用任一事件侦听器并刷新页面。这引起了我们的注意,因为我们希望在刷新时看到控制台中的日志,但我们所看到的只是“SW激活”消息。事实证明,当选中重新加载选项时,Chrome会刷新页面两次。

您可以通过勾选“控制台设置”面板中的“保留日志”复选框并再次刷新来确认。您应该看到记录的安装和激活事件,以及'foo',然后是'导航到http:// localhost:3000 /'以指示页面已重新加载,然后是最终的“SW激活”消息。

07.跟踪获取事件

是时候添加另一个监听器了。这次我们将跟踪每次页面加载资源时触发的事件,例如CSS文件,图像甚至API响应。我们将打开一个缓存,将请求响应返回给页面,然后 - 在后台 - 缓存响应。首先让我们添加监听器并刷新,这样你就可以看到会发生什么。在控制台中你应该看到很多FetchEvent日志。

self.addEventListener('fetch',(event)=> {console.log(event);});

我们的服务模式使用BrowserSync,它将自己的脚本添加到页面并发出websocket请求。你也会看到这些的FetchEvents,但我们想忽略它们。我们也只想缓存来自我们自己域的GET请求。因此,让我们添加一些内容来忽略不需要的请求,包括明确忽略/索引路径:

self.addEventListener('fetch',(event)=> {//忽略跨域请求if(!event.request.url.startsWith(self.location.origin)){return;} //忽略非GET请求if( event.request.method!=='GET'){return;} //忽略浏览器同步if(event.request.url.indexOf('browser-sync')> -1){return;} //防止索引如果(event.request.url ===(self.location.origin +'/')){return;} //阻止index.html被缓存if(event.request.url.endsWith('index)。 html')){return;} console.log(event);});

现在日志应该更清洁,开始缓存是安全的。

08.缓存资产

现在我们可以开始缓存这些响应。首先,我们需要为缓存命名。我们打电话给我们V1资产。将此行添加到sw.js文件的顶部:

const assetsCacheName ='v1-assets';

然后我们需要劫持FetchEvents,这样我们就可以控制返回页面的内容。我们可以使用事件来做到这一点RespondWith方法。此方法接受Promise,因此我们可以添加此代码,替换的console.log

//告诉fetch响应这个Promise链event.respondWith(//打开缓存caches.open(assetsCacheName).then((cache)=> {//向网络请求返回获取(event.request) .then((response)=> {//缓存响应cache.put(event.request,response.clone()); //将原始响应返回给页面返回响应;});}));;

这会将请求转发到网络,然后将响应存储在缓存中,然后将原始响应发送回页面。

值得注意的是,在用户第二次加载页面之前,此方法实际上不会缓存响应。第一次将安装并激活工人,但到时候了听众准备好了,一切都已经被要求了。

刷新几次并在DevTools> Application选项卡中检查缓存。展开左列中的“缓存存储”树,您应该看到包含所有存储响应的缓存。

09.从缓存中服务

一切都被缓存,但我们实际上并没有使用缓存来提供任何文件。我们现在就把它搞定。首先,我们将在缓存中查找请求的匹配项,如果存在,我们将为此提供服务。如果它不存在,我们将使用网络,然后缓存响应。

//告诉fetch响应此链事件。响应(//打开缓存caches.open(assetsCacheName).then((cache)=> {//在缓存中查找匹配请求返回cache.match(事件。请求).then((matched)=> {//如果找到匹配,则首先返回缓存版本if(匹配){return matched;} //否则继续返回网络返回fetch(event.request).then((响应)=> {//缓存响应cache.put(event.request,response.clone()); //将原始响应返回给页面返回响应;});});}));;

保存文件并刷新。检查DevTools> Network选项卡,您应该看到(从ServiceWorker)列出的每个静态资产的Size列。

哎呀,我们完成了。对于如此少量的代码,需要了解很多。您应该看到,一旦缓存了所有资产,刷新页面非常活泼,但让我们在限制连接(DevTools>网络选项卡)上快速(不科学地)检查加载时间。

如果没有服务工作者,通过模拟的快速3G网络加载大约需要30秒才能加载所有内容。对于Service Worker,具有相同的限制连接但从缓存加载,只需不到一秒钟。

检查离线框并刷新,您还会看到页面在没有连接的情况下加载,但我们无法从API获取预测数据。在第2页,我们将返回到此并学习如何缓存API响应。

下一页:使用Service Worker提供在线访问



翻译字数超限