su37josephxia/frontend-interview

Day30 - 详细说一个ServiceWorker的应用

su37josephxia opened this issue · 16 comments

先解释一下什么是 ServiceWorker

一个服务器与浏览器之间的中间人角色,如果网站中注册了service worker那么它可以拦截当前网站所有的请求,进行判断(需要编写相应的判断程序),如果需要向服务器发起请求的就转给服务器,如果可以直接使用缓存的就直接返回缓存不再转给服务器。从而大大提高浏览体验。

其实pwa就是一种ServiceWorker。我们常见的vue官网,nuxt官网都是使用了ServiceWorker。

Service worker本质上充当Web应用程序、浏览器与网络(可用时)之间的代理服务器,这个API旨在创建有效的离线体验,它会拦截网络请求并根据网络是否可用来采取适当的动作、更新来自服务器的的资源,它还提供入口以推送通知和访问后台同步API。

定义
service worker 是一个注册在指定源和路径下的事件驱动 worker。它采用 JavaScript 控制关联的页面或者网站,拦截并修改访问和资源请求,细粒度地缓存资源。Service worker 运行在 worker 上下文,因此它不能访问 DOM。由于它运行在 worker 中,所以不会造成阻塞。但是出于安全考量,Service Worker 只能由 HTTPS 承载,因为修改网络请求的能力如果被中间人利用,将会非常危险。
使用
注册
通过 ServiceWorkerContainer.register() 方法首次注册 service worker。如果注册成功,service worker 就会被下载到客户端并尝试安装或激活,它将作用于整个域内用户可访问的 URL,或者其特定子集。

if ('ServiceWorker' in navigator) {
   window.addEventListener('load', function () {
      /* 创建并指定对应的执行内容 */
            /* scope 参数是可选的,可以用来指定你想让 service worker 控制的内容的子目录。 在这个例子里,我们指定了 '/',表示 根网域下的所有内容。这也是默认值。 */
            navigator.serviceWorker.register('./serviceWorker.js', {scope: './'})
                .then(function (registration) {
                    console.log('ServiceWorker registration successful with scope: ', registration.scope);
                })
                .catch(function (err) {
                    console.log('ServiceWorker registration failed: ', err);
                });
   })
}


/* 监听安装事件,install 事件一般是被用来设置你的浏览器的离线缓存逻辑 */
this.addEventListener('install', function (event) {
 	
    /* 通过这个方法可以防止缓存未完成,就关闭serviceWorker */
    event.waitUntil(
        /* 创建一个名叫V1的缓存版本 */
        caches.open('v1').then(function (cache) {
            /* 指定要缓存的内容,地址为相对于跟域名的访问路径 */
            return cache.addAll([
                './index.html'
            ]);
        })
    );
});

/* 注册fetch事件,拦截全站的请求 */
this.addEventListener('fetch', function(event) {
  event.respondWith(
    // magic goes here
      /* 在缓存中匹配对应请求资源直接返回 */
    caches.match(event.request)
  );
});

Service worker 用于作为浏览器和后台服务器中间的代理服务器,用来提供更好的离线体验。它基于 web worker,可以拦截浏览器的网络请求,运行我们预设的处理逻辑,比如当前请求资源本地有缓存,则直接返回缓存内容,从而可以实现有效的离线体验。

一个应用场景就是实现 PWA(Progressive Web App),可以简单理解为拥有类似原生体验的 web 网页应用,而且无需下载安装。实际的例子有 抖音和Goole地图等。

service worker 常用的就是做静态资源的缓存。另一个应用场景的话,因为 service worker 可以拦截请求,所以可以做一层代理。包括统计也好,转发请求也行,还能在客户端写假请求,然后根据不同的 service worker 来处理接口调用。他是依赖于worker的,所以有不阻塞js的效果,那可以用于需要进行大量计算的请求。

  • Service Worker 基于 Web Worker 事件驱动。
    • Service Worker 同样可以在浏览器后台挂起新线程,来缓解 JavaScript 的单线程问题。并且,我们可以用 Service Worker 拦截网络请求进行本地缓存或请求转发,相当于充当服务端与浏览器、浏览器与 Web 应用程序之间的代理服务器
  • 应用场景-缓存
    • 应用缓存主要是通过manifest文件来注册被缓存的静态资源,已经被废弃,因为他的设计有些不合理的地方,他在缓存静态文件的同时,也会默认缓存html文件。这导致页面的更新只能通过manifest文件中的版本号来决定。所以,应用缓存只适合那种常年不变化的静态网站。如此的不方便,也是被废弃的重要原因。
    • PWA也运用了该文件,不同于manifest简单的将文件通过是否缓存进行分类,PWA用manifest构建了自己的APP骨架,并运用Servie Worker来控制缓存
  • Service Worker一个典型应用是PWA(Progressive Web App)。
  • Service Worker是谷歌发起的实现PWA(Progressive Web App)的一个关键角色,PWA是为了解决传统Web APP的缺点:
  1. 没有桌面入口
  2. 无法离线使用
  3. 没有Push推送
  • 工作原理:Service Worker是在后台启动的一条服务Worker线程,这个Worker的工作是把一些资源缓存起来,然后拦截页面的请求,先看下缓存库里有没有,如果有的话就从缓存里取,响应200,反之没有的话就走正常的请求。

提到 Service Worker ,不得不先介绍一下 Web Worker,众所周知 javaScript 是执行在单线程的,如果执行大量计算任务就会堵塞了前端的渲染。而通过独立的线程的能力,Web Worker 可以分解耗时的任务。但是它的功能不应只局限于此,Service Worker 便是在 Web Worker 的基础上增加了离线缓存的能力。
Service Worker 可以拦截处理页面的所有网络请求,可以使用 cache 和 indexDB 的 api,可以让开发者自己主动管理缓存的内容以及版本,为离线弱网环境下的 web 的运行提供了可能,让 web 在体验上更加贴近 native。
应用场景:
预加载 && 离线化,语雀的官网就是用该技术

详细谈谈serviceWorker应用之一: web消息推送
因为传统的web页面缺少象app一样的离线推送功能,而serviceWorker的出现恰巧能弥补这方面的一些缺陷
详细说下serviceWorker是如何实现web消息推送的
● 浏览器先向用户发送是否订阅消息,询问用户是否同意,同意的话会进行以下步骤
● 浏览器请求FCM服务器获取一个用户订阅信息,SW接收到用户订阅信息后,将其发送到我们自己的服务器存储起来
● 我们的服务器通过web push的api和用户的订阅信息可以向FCM服务器发送消息
● FCM服务器再下发到对应的浏览器
● 此时对应的浏览器会触发SW的push事件,我们接收到这个push事件后,可以调用showNotification 这个api显示这个push信息的内容
● 最后,操作系统就会显示这个消息推送(操作系统是否屏蔽的浏览器消息推送)

上面提到的FCM即firebase是google的一种跨平台消息传递解决方案, 可供免费传递消息, 当然因为这个是google的服务器,国内用户因为墙无法请求的到FCM的信息
总的来说Service Worker让我们在Web端也能有像原生APP一样的Push通知,使得Web端越来越像原生APP端

服务工作者线程(service worker)是一种类似浏览器中代理服务器的线程,可以拦截外出请求和缓存响应。
两个最有用的作用:充当网络请求的缓存层和启用推送通知。

Service Worker 是一个浏览器中的进程,它在被注册安装之后,能够被在多个页面中使用,也不会因为页面的关闭而被销毁。
出于对安全问题的考虑,Service Worker 只能被使用在 https 或者本地的 localhost 环境下

缓存响应:
Service Worker的一大应用是可以利用CacheStorage API来缓存js、css、字体、图片等静态文件。我们可以在Service Worker的install阶段,指定需要缓存的具体文件,在fetch事件的回调函数中,检查请求的url,如果匹配了已缓存的资源,则不再从服务端获取,以此达到提升网页性能的目的。
利用它可以更精准地、以编码方式控制缓存,如何缓存、缓存什么、如何更新缓存,完全取决于代码如何写,所以这提供了很大的自由度,但同时也带来维护成本

  • javascript执行时单线程的,Service Worker可以启动另一条线程,可以去加载一些资源缓存起来,然后监听fetch事件,在这个事件里拦截页面的请求,先看下缓存里有没有,如果有直接返回,否则正常加载。或者是一开始不缓存,每个资源请求后再拷贝一份缓存起来,然后下一次请求的时候缓存里就有了。
  • 语雀应用Service Worker缓存字体,样式表等

Service Worker运行于浏览器后台,不受页面刷新的影响,可以监听和截拦作用域范围内所有页面的 HTTP 请求。同时serviceWorker也是pwa的核心,可以在基于浏览器的 web 应用中实现如离线缓存、消息推送、静默更新等 native 应用常见的功能,以给 web 应用提供更好更丰富的使用体验。

Service workers 本质上充当 Web 应用程序、浏览器与网络(可用时)之间的代理服务器。这个 API 旨在创建有效的离线体验,它会拦截网络请求并根据网络是否可用来采取适当的动作、更新来自服务器的的资源。它还提供入口以推送通知和访问后台同步 API。

workbox 是 GoogleChrome 团队推出的一套 Web App 静态资源和请求结果的本地存储的解决方案,该解决方案包含一些 Js 库和构建工具, workbox的背后就有 Service Worker

一个服务器与浏览器之间的中间人角色,如果网站中注册了service worker那么它可以拦截当前网站所有的请求,进行判断(需要编写相应的判断程序),如果需要向服务器发起请求的就转给服务器,如果可以直接使用缓存的就直接返回缓存不再转给服务器。从而大大提高浏览体验。
一个应用场景就是实现 PWA(Progressive Web App)离线应用。

PWA是Web应用技术向native应用的自然眼神,而Service Worker是PWA的关键;
Service Worker是浏览器在后台独立于网页运行的、用JavaScript编写的脚本,是浏览器对象中navigator下的一个属性,具有拦截浏览器HTTP请求的能力,搭配CacheStorage,用在PWA中,可以提升Web应用在网络条件不佳甚至离线时的用户体验和性能;

具体使用方式:

  1. 可以利用CacheStorage API来缓存js、css、字体、图片等静态文件。我们可以在Service Worker的install阶段,指定需要缓存的具体文件,在监听的fetch事件的回调函数中,检查请求的url,如果匹配了已缓存的资源,则不再从服务端获取,以此达到提升网页性能的目的。
  2. 可以用来缓存页面,比如说我们可以监听进入页面的请求,缓存一个类似404的页面,如果是离线状态就直接返回该页面给用户,缓存的页面可以根据自己的特定需求来设计。

操作过程:

  1. 手写service-worker.js的文件,在其中监听fetch请求,匹配url,执行自己需要的操作
  2. 通过监听load事件,navigator.serviceWorker.register 注册service-worker.js文件
  3. 第一次进入网页只是下载和注册service-worker文件,第二次进入才会处理监听fetch请求

service worker是一个浏览器和服务器之间的一个代理服务器,它能够拦截指定页面内的所有http请求包括资源请求,并可以将指定的资源缓存下来。

应用场景

PWA渐进式web应用

PWA简单来说,就是让web页面可以独立于浏览器app,单独有一个桌面入口,并且可以离线使用。
那么PWA的离线使用就是用到了service worker。
离线页面,就是在无网的状态下也能访问,并能满足一部分需求。原理就是在第一次访问页面的时候,我们去给该页面注册安装对应的service worker,后序再访问这个页面的时候,我们可以在service woker上拦截并判断,如果当前http请求失败,那么去我们缓存的内容列表里将对应的内容返回过去,这样就可以做到,如果网络异常,由于该页面资源都被缓存了,我们仍然可以正常访问,看到页面内容。
那PWA就是利用了service worker的这个请求代理功能,将资源缓存在本地,下次进来没有网络,就直接使用缓存内对应的资源。