资讯专栏INFORMATION COLUMN

service worker的基本知识

guqiu / 2398人阅读

摘要:需要提前掌握的基本使用检测是否存在,创建缓存总对象。之后更新后的启动并触发事件。此时,当前页面生效的依然是老版本的,新的会进入状态。当页面关闭之后,老的会被干掉,新的接管页面一旦新的生效后会触发事件。

前言: 看到一篇讲解service worker的文章, 基础讲的还不错, 所以转了以后作为自己的参考

Service Worker是什么

service worker 是独立于当前页面的一段运行在浏览器后台进程里的脚本。它的特性将包括推送消息,背景后台同步, geofencing(地理围栏定位),拦截和处理网络请求。

这个 API 会让人兴奋的原因是,它可以使你的应用先访问本地缓存资源,所以在离线状态时,在没有通过网络接收到更多的数据前,仍可以提供基本的功能(一般称之为 Offline First)。

在 service worker 之前,另一个叫做 APP Cache 的 api 也可以提供离线体验。APP Cache 的的主要问题是坑比较多,而且其被设计为只适合于单页 web 应用程序,对于传统的多页网站则不适合。service worker 的设计规避了这些痛点。

关于 service worker 的一些注意点:

service worker 是一个JavaScript worker ,所以它不能直接访问 DOM 。但 service worker 可以通过postMessage 接口与跟其相关的页面进行通信,发送消息,从而让这些页面在有需要的时候去操纵 DOM 。

Service worker 是一个可编程的网络代理,允许你去控制如何处理页面的网络请求, 可以处理fetch请求。

Service worker 在不使用时将被终止,并会在需要的时候重新启动,因此你不能把onfetch 和onmessage事件来作为全局依赖处理程序。如果你需要持久话一些信息并在重新启动Service worker后使用他,可以使用 IndexedDBAPI ,service worker 支持。

Service Worker 的缓存机制是依赖 Cache API 实现的 Service worker 广泛使用了 promise。

Service worker依赖 HTML5 fetch API

Service Workers 要求必须在 HTTPS 下才能运行

Service Worker生命周期

注册service worker,在网页上生效

安装成功,激活 或者 安装失败(下次加载会尝试重新安装)

激活后,在sw的作用域下作用所有的页面,首次控制sw不会生效,下次加载页面才会生效。

sw作用页面后,处理fetch(网络请求)和message(页面消息)事件 或者 被终止(节省内存)。

需要提前掌握的API

Cache API基本使用

(1)检测api是否存在

if("caches" in window) {
        // Has support!
    }

(2)caches.open,创建缓存总对象。如下创建名为 test-cache 的缓存。

caches.open("test-cache").then(function(cache) {
        // Cache is created and accessible
    });

(3)cache.add和cache.addAll,添加缓存内容。其中cache.add只添加一个,cache.addAll可以添加多个。

caches.open("test-cache").then(function(cache) { 
        cache.addAll(["/", "/images/logo.png"])
        .then(function() { 
        // Cached!
        
        // or use cache.add
        cache.add("/page/1");  // "/page/1" URL will be fetched and cached!
     });
    });

(4)cache.keys(),查看已经缓存的数据

caches.open("test-cache").then(function(cache) {
        cache.keys().then(function(cachedRequests) {
            console.log(cachedRequests); // [Request, Request]
        });
    });

(5)cache.match和cache.matchAll,匹配缓存文件路径

caches.open("test-cache").then(function(cache) {
        cache.match("/page/1").then(function(matchedResponse) {
            console.log(matchedResponse);
        });
    });

(6)cache.delete,删除缓存。

caches.open("test-cache").then(function(cache) {
       cache.delete("/page/1");
   });

Fetch API基本使用

// url (required), options (optional)
fetch("https://davidwalsh.name/some/url", {
    method: "get"
}).then(function(response) {
     
}).catch(function(err) {
    // Error :(
});

其中options对象包含以下属性:

method - GET, POST, PUT, DELETE, HEAD

url - 请求的链接

headers - 请求的header对象

referrer - 请求的referrer对象

mode - cors, no-cors, same-origin

credentials - 设置请求可不可以携带cookie

redirect - follow, error, manual

integrity - 子资源完整值

cache - 缓存模式 (default, reload, no-cache)


 可以在fetch中传入Request对象实例:

var request = new Request("https://davidwalsh.name/users.json", {
    method: "POST",
    mode: "cors",
    redirect: "follow",
    headers: new Headers({
        "Content-Type": "text/plain"
    })
});
 
// Now use it!
fetch(request).then(function() { /* handle response */ });

可以自定义返回的Response对象实例,其中的options有:

type - basic, cors

url

useFinalURL - 上面的url参数是不是最终的URL

status - 状态码(ex: 200, 404, etc.)

ok - 是否成功响应 (范围在 200-299)

statusText - 状态码 (ex: OK)

headers - 响应的headers对象

另外Response的实例还具备以下方法:

clone() - 创建Response对象的克隆。

error() - 返回与网络错误关联的新Response对象。

redirect() - 使用不同的URL创建新响应。

arrayBuffer() - 返回使用ArrayBuffer解析的promise。

blob() - 返回使用Blob解析的promise。

formData() - 返回使用FormData对象解析的promise。

json() - 返回使用JSON对象解析的promise。

text() - 返回使用USVString(文本)解析的promise。

// Create your own response for service worker testing
// new Response(BODY, OPTIONS)
var response = new Response(".....", {
    ok: false,
    status: 404,
    url: "/"
});
 
// The fetch"s `then` gets a Response instance back
fetch("https://davidwalsh.name/")
    .then(function(responseObj) {
        console.log("status: ", responseObj.status);
    });
Service Worker的使用

兼容低版本,注入Cache API的一个polyfill,Service Worker需要依赖Cache API:

self.importScripts("./serviceworker-cache-polyfill.js");

注册service worker:

if ("serviceWorker" in navigator) {
  navigator.serviceWorker.register("/sw.js").then(function(registration) {
    // Registration was successful
    console.log("ServiceWorker registration successful with scope: ",    registration.scope);
  }).catch(function(err) {
    // registration failed :(
    console.log("ServiceWorker registration failed: ", err);
  });
}

上面的代码检查 service worker API 是否可用,如果可用, /sw.js 这个文件将会作为 service worker 被注册。

如果这个 service worker 已经被注册过,浏览器会自动忽略上面的代码。

有一个特别要注意是 service worker 文件的路径。你一定注意到,在这个例子中,service worker 文件被放在这个域的根目录下,这意味着 service worker是跟网站同源的。换句话说,这个 service worker 将会获取到这个域下的所有 fetch 事件。如果 service worker文件注册到/example/sw.js ,那么 service worker 只能收到 /example/ 路径下的 fetch 事件(比如: /example/page1/, /example/page2/)。

安装service worker:

var CACHE_NAME = "my-site-cache-v1";
var urlsToCache = [
  "/",
  "/styles/main.css",
  "/script/main.js"
];
 
self.addEventListener("install", function(event) {
  // Perform install steps
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(function(cache) {
        console.log("Opened cache");
        return cache.addAll(urlsToCache);
      })
  );
});

上面代码声明了需要缓存的内容,如果所有的文件都缓存成功,service worker 就安装成功了。如果任何一个文件下载失败,那么安装步骤就会失败。这个方式依赖于你自己指定的资源,但这意味着,你需要非常仔细地确定哪些文件需要被缓存。指定了太多文件的话,会增加失败率。

对缓存跟返回请求的处理

self.addEventListener("fetch", function(event) {
  event.respondWith(
    caches.match(event.request)
      .then(function(response) {
        // Cache hit - return response
        if (response) {
          return response;
        }
 
    // IMPORTANT: Clone the request. A request is a stream and
    // can only be consumed once. Since we are consuming this
    // once by cache and once by the browser for fetch, we need
    // to clone the response
    var fetchRequest = event.request.clone();
 
    return fetch(fetchRequest).then(
      function(response) {
        // Check if we received a valid response
        if(!response || response.status !== 200 || response.type !== "basic") {
          return response;
        }
 
        // IMPORTANT: Clone the response. A response is a stream
        // and because we want the browser to consume the response
        // as well as the cache consuming the response, we need
        // to clone it so we have 2 stream.
        var responseToCache = response.clone();
 
        caches.open(CACHE_NAME)
          .then(function(cache) {
            cache.put(event.request, responseToCache);
          });
 
        return response;
      }
    );
  })
);

如果我们想在缓存中添加新的请求缓存,可以通过处理fetch请求的response,将其添加到缓存中即可。代码里我们做了以下事情:

添加一个 callback 到 fetch 请求的 .then 方法中。 一旦我们获得一个 response,我们进行如下的检查:

1. 确保 response 有效
2. 检查 response 的状态是200
3. 确保 response 的类型是 basic 类型的,这说明请求是同源的,这意味着第三方的请求不能被缓存。

如果检查通过会clone 这个请求。这么做的原因是如果 response 是一个 Stream,那么它的 body 只能被消费一次。所以为了让浏览器跟缓存都使用这个body,我们必须克隆这个 body,一份到浏览器,一份到缓存中缓存。

重新激活

你的 service worker 总会有要更新的时候。在那时,你需要按照以下步骤来更新:

更新你 service worker 的 JavaScript 文件 当用户浏览你的网站时,浏览器尝试在后台重新下载 service worker 的脚本文件。经过对比,只要服务器上的文件和本地文件有一个字节不同,这个文件就认为是新的。

之后更新后的 service worker 启动并触发 install 事件。此时,当前页面生效的依然是老版本的 service worker,新的 service worker 会进入 “waiting” 状态。

当页面关闭之后,老的 service worker 会被干掉,新的 servicer worker 接管页面 一旦新的 service worker 生效后会触发 activate 事件。 通常来讲,需要在 activate 的 callback 中进行 cache 管理,来清理老的 cache。我们在 activate 而不是 install 的时候进行的原因,是如果我们在 install 的时候进行清理,那么老的 service worker 仍然在控制页面,他们依赖的缓存就失效了,因此就会突然被停止。

之前我们使用的缓存可以叫 my-site-cache-v1 ,我们想把这个拆封到多个缓存,一份给页面使用,一份给博客文章使用。这意味着,install 步骤里,我们要创建两个缓存: pages-cache-v1 和 blog-posts-cache-v1。在 activite 步骤里,我们需要删除旧的 my-site-cache-v1。

下面的代码会遍历所有的缓存,并删除掉不在 cacheWhitelist 数组(我们定义的缓存白名单)中的缓存。

self.addEventListener("activate", function(event) {
 
  var cacheWhitelist = ["pages-cache-v1", "blog-posts-cache-v1"];
 
  event.waitUntil(
    caches.keys().then(function(cacheNames) {
      return Promise.all(
        cacheNames.map(function(cacheName) {
          if (cacheWhitelist.indexOf(cacheName) === -1) {
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
});

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/62107.html

相关文章

  • pwa-之service worker 基本概念

    摘要:是浏览器和服务器之间的脚本,主要作用是拦截请求,修改响应,以及一些其他的作用。这是出于安全因素的考虑。这个注册的过程是发生在之外的。在安装完成,激活之前,它不会拦截任何请求。将会始终拦截请求,重启页面也是为了这个。 pwa-之service worker 基本概念pwa-之service worker 离线文件处理 学习service worker 基本概念 在本章,将涵盖以下内容 ...

    galaxy_robot 评论0 收藏0
  • pwa-之service worker 基本概念

    摘要:是浏览器和服务器之间的脚本,主要作用是拦截请求,修改响应,以及一些其他的作用。这是出于安全因素的考虑。这个注册的过程是发生在之外的。在安装完成,激活之前,它不会拦截任何请求。将会始终拦截请求,重启页面也是为了这个。 pwa-之service worker 基本概念pwa-之service worker 离线文件处理 学习service worker 基本概念 在本章,将涵盖以下内容 ...

    olle 评论0 收藏0
  • service worker基本知识

    摘要:需要提前掌握的基本使用检测是否存在,创建缓存总对象。之后更新后的启动并触发事件。此时,当前页面生效的依然是老版本的,新的会进入状态。当页面关闭之后,老的会被干掉,新的接管页面一旦新的生效后会触发事件。 前言: 看到一篇讲解service worker的文章, 基础讲的还不错, 所以转了以后作为自己的参考 Service Worker是什么 service worker 是独立于当前页面...

    int64 评论0 收藏0
  • Service Worker

    摘要:当下比较火,而是实现的一项关键技术,今天我们一起了解下关于的一些基础知识和适用场景。资源查找顺序为从左向右,找到资源则返回,未找到则继续寻找,直至最终获取资源。借助,真正让应用变得流行,也许就在不久的将来。 当下PWA比较火,而Service Worker是实现PWA的一项关键技术,今天我们一起了解下关于Service Worker的一些基础知识和适用场景。 什么是Server Wor...

    Baaaan 评论0 收藏0
  • JavaScript 是如何工作Service Worker 生命周期及使用场景

    摘要:的生命周期的生命周期与页面完全分离。换句话说,这个将为这个域中的所有内容接收事件。这不是必要的,但绝对是推荐的。新的将启动并且安装事件将被移除。使用,可以很容易被劫持连接并伪造响应。后台同步允许延迟操作,直到用户具有稳定的连接。 这是专门探索 JavaScript 及其所构建的组件的系列文章的第8篇。 想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你! 如果你错过了前...

    zzzmh 评论0 收藏0

发表评论

0条评论

guqiu

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<