此处转载请参考原文,略有删节,本文根据知识共享署名4.0国际许可协议共享,BYTroland。本系列持续更新中,Github地址请查看这里。这是JavaScript工作原理的第8章。也许您已经知道ProgressiveWebApps只会越来越受欢迎,因为它旨在创建具有更流畅用户体验的Web应用程序,并创建类似原生应用程序的体验,而不是类似浏览器的外观和感觉。构建ProgressiveWebApp的主要要求之一是能够在各种网络和数据加载条件下使用——它可以在网络不稳定或没有网络时使用。在本文中,我们将深入探讨ServiceWorker:它们的工作原理以及您应该关注的事项。最后,我们将列举一些ServiceWorkers可以利用的独特优势,分享我们在SessionStack中的团队实践经验。概述要了解有关ServiceWorkers的一切,您应该首先阅读之前发布的关于WebWorkers的文章。总的来说,ServiceWorker是一种WebWorker,更准确地说,它更像是一个SharedWorker。ServiceWorker运行在其全局脚本上下文中未指定并绑定到网页无法访问DOMServiceWorker接口令人兴奋的原因之一是它支持离线运行Web应用程序,这使开发人员可以完全控制Web应用程序的行为的。生命周期ServiceWorker的生命周期与网页完全无关。它包括以下步骤:下载安装激活下载当浏览器下载包含ServiceWorker相关代码的.js文件时会发生这种情况。安装为了在Web应用程序中使用ServiceWorker,必须首先在JavaScript代码中注册它。serviceworker注册后,会让浏览器在后台开始安装serviceworker的过程。通过注册服务工作者,浏览器知道包含与服务工作者相关代码的JavaScript文件。看下面的代码:registerSuccessconsole.log('ServiceWorkerregistrationsuccessful');},function(err){//注册失败console.log('ServiceWorkerregistrationfailed:',err);});});}上面的代码首先检查当前执行环境是否支持ServiceWorkerAPI。如果是,请注册/sw.jsServiceWorker。您可以在每次页面加载时任意调用register()-浏览器将检查serviceworker是否已经注册并适当地处理它。register()方法中需要特别注意的地方是ServiceWorker文件的地址。当前示例位于服务器根目录中。这意味着serviceworker将作用于整个源地址。也就是说serviceworker会收到该域名下所有页面的fetch事件。如果注册的serviceworker的文件路径是/example/sw.js,那么serviceworker会收到所有页面路径以/example/开头的URL地址的fetch事件(比如/example/page1//example/page2/).在安装阶段,加载和缓存一些静态资源是个好主意。成功缓存静态资源后,ServiceWorker安装完成。如果加载失败-ServiceWorker将重试。安装成功后,静态资源缓存成功。这也回答了为什么ServiceWorker应该在加载事件之后注册。这不是必需的,但强烈推荐。你为什么要这样做?假设用户第一次访问Web应用程序。ServiceWorker还没有注册,浏览器没有办法提前知道它最终是否会安装它。如果已安装,浏览器将为添加的线程分配额外的CPU和内存,否则这些线程将用于呈现网页。这里供参考,load事件会在图片、样式等所有资源加载完毕后触发。最终的结果是,如果在页面中安装了ServiceWorker,可能会导致页面延迟加载和渲染——它不会让用户尽快访问网页。请注意,这只会在第一次访问页面时发生。随后的页面访问不会受到ServiceWorker安装的影响。一旦ServiceWorker在第一次页面访问时被激活,它就可以处理由后续页面访问触发的页面加载/缓存事件。没错,ServiceWorkers需要很好地加载才能处理有限的网络带宽。激活安装好之后,下一步就是激活了。此步骤是在操作之前缓存资源的绝佳时机。一旦激活,ServiceWorker就可以开始控制其范围内的所有页面。一个有趣的事实是,注册到ServiceWorker的页面在再次加载之前不会被ServiceWorker处理。当ServiceWorker开始接管控制时,它有以下状态:处理网络触发的fetch和消息事件或来自页面的消息请求Abort以节省内存以下是它的生命周期:处理ServiceWorker内部的安装过程运行注册在页面上ServiceWorker进程中,我们看一下ServiceWorker脚本中发生了什么,它监听了ServiceWorker实例的install事件。处理install事件需要以下步骤:打开cache缓存文件,确认是否缓存了所有静态资源下面是一个ServiceWorker内部可能的简单安装代码:varCACHE_NAME='my-web-app-cache';varurlsToCache=['/','/styles/main.css','/scripts/app.js','/scripts/lib.js'];self.addEventListener('install',function(event){//event.waitUntil使用promise获取安装时长和是否安装失败event.waitUntil(caches.open(CACHE_NAME).then(function(cache){console.log('Openedcache');returncache.addAll(urlsToCache);}));});如果文件缓存成功,则ServiceWorker安装成功。如果任何文件下载失败,ServiceWorker将无法安装。所以要小心需要缓存的文件。处理安装事件是完全可选的。不处理时,跳过上述步骤即可。这部分只有在缓存运行时请求它时才干。在这里您可以看到如何拦截请求并返回创建的缓存(并创建一个新缓存)。当安装了ServiceWorker并且用户导航到另一个页面或刷新当前页面时,ServiceWorker将收到一个fetch事件。这是一个示例,演示如何返回缓存的静态资源或执行新请求并缓存返回的结果:self.addEventListener('fetch',function(event){event.respondWith(//该方法查询请求然后返回ServiceWorker创建的任何缓存数据。caches.match(event.request).then(function(response){//如果有缓存,则返回if(response){returnresponse;}//复制请求,请求是流,只能使用once.因为之前已经被缓存使用过一次,所以为了在浏览器中使用fetch,我们需要克隆请求varfetchRequest=event.request.clone();//没有找到缓存。所以我们需要执行fetch发起请求并返回请求数据returnfetch(fetchRequest).then(function(response){//检查返回数据是否合法if(!response||response.status!==200||response.type!=='basic'){returnresponse;}//复制返回数据,因为它也是一个流,既然我们要浏览器既要使用返回数据又要缓存,所以必须复制。所以有两个流varresponseToCache=response.clone();caches.open(CACHE_NAME).then(function(cache){//将请求添加到缓存中以供以后查询cache.put(event.request,responseToCache);});返回响应;});}));});大致流程如下:event.respondWith()会决定如何响应fetch事件。caches.match()查询请求,然后返回先前创建的缓存中的任何缓存数据并返回一个承诺。如果是,则返回缓存的数据。否则,执行提取。检查返回的状态码是否为200,同时检查响应类型是否为basic,即检查请求是否在同域。当前场景不缓存对第三方资源的请求。将返回的数据添加到缓存中。由于请求和响应都是流,流数据只能被消费一次,所以必须进行复制。由于缓存和浏览器都需要使用它们,因此必须复制它们。更新ServiceWorker当用户访问web应用时,浏览器会在后台尝试重新下载包含ServiceWorker代码的.js文件。如果下载的文件与当前的ServiceWorker代码文件略有不同,浏览器会认为该文件发生了变化并创建一个新的ServiceWorker。创建新ServiceWorker的过程将开始,然后触发install事件。但是此时,旧的ServiceWorker仍在控制Web应用程序页面,这意味着新的ServiceWorker将处于等待状态。一旦Web应用程序当前打开的页面关闭,旧的ServiceWorker将被浏览器杀死,新的ServiceWorker可以被启动。这时候就会触发activate事件。为什么所有这些都是必要的?这是为了避免在不同的选项卡中同时运行不同版本的web应用程序而导致的问题-一些实际存在于网页中并可能引入新错误的问题(例如在浏览器本地存储数据时具有不同的数据库结构)).从缓存中删除数据激活回调中最常见的步骤是缓存管理。因为如果在安装步骤中要删除旧的缓存,这会导致ServiceWorkers无法获取到缓存中的文件数据,这时候就需要进行缓存管理了。这是一个如何删除不在白名单中的缓存的示例(在本例中,以page-1或page-2命名):self.addEventListener('activate',function(event){varcacheWhitelist=['page-1','page-2'];event.waitUntil(//获取缓存中的所有keycaches.keys().then(function(cacheNames){returnPromise.all(//遍历所有缓存文件cacheNames.map(function(cacheName){//如果缓存文件不在白名单中,则删除它if(cacheWhitelist.indexOf(cacheName)===-1){returncaches.delete(cacheName);}}));}));});HTTPS要求ServiceWorkers在开发时通过localhost可用,但在发布时必须部署HTTPS(这是使用HTTPS的最后一个原因)。ServiceWorker可用于劫持网络连接和伪造响应数据。如果没有HTTPS,Web应用程序很容易受到中间人攻击。为保证安全,ServiceWorker必须通过HTTPS在页面上进行注册,这样浏览器接收到的ServiceWorker在传输过程中不被篡改。浏览器支持ServiceWorkers具有良好的浏览器兼容性。您可以跟踪所有浏览器的支持进度:https://jakearchibald.github....ServiceWorkers提供了更多可能是ServiceWorkers独有的功能:推送通知-允许用户选择定期接收来自Web应用程序的推送通知intervalsUpdateBackgroundSync-允许延迟操作直到网络连接稳定之后。这可确保用户立即发送数据。周期同步(未来支持)-提供管理周期性后台数据同步的功能地理围栏(未来支持)-可以自定义参数,即地理围栏,包含用户感兴趣的区域。当设备穿过地理围栏时接收通知,使您能够根据用户的地理位置提供有用的用户体验。此处提到的每个功能将在本系列的后续文章中进行详细说明。我们一直在努力使SessionStack交互体验尽可能流畅,优化页面加载时间和响应时间。当在SessionStack上回放用户会话或直播时,SessionStack接口会不断地从服务器获取数据,为用户创造一个类似缓冲的体验(类似于视频缓冲)。详细了解一些原理,一旦将SessionStack库集成到Web应用程序中,它将继续收集DOM更改、用户交互、网络请求、未处理的异常和调试信息等数据。在实时回放或观看会话时,SessionStack将返回所有数据,以方便观察用户浏览器中发生的所有事件。(视觉上和技术上)。所有这一切都立即发生,因为我们不想让用户等待。由于数据是由前端获取的,此时可以使用ServiceWorker来处理玩家重新加载和再次流式传输所有数据等情况。处理慢速网络连接也很重要。请参阅维基百科对流的定义,以便更好地理解流的概念。本系列持续更新中,Github地址请查看这里。
