service worker 的运用与实践

2024-01-02  本文已影响0人  林思念
Service Worker 简介

Service Workers 本质上是一种能在浏览器后台运行的独立线程,它能够在网页关闭后持续运行,能够拦截网络请求并根据网络是否可用来采取适当的动作、更新来自服务器的的资源,从而实现拦截和加工网络请求、消息推送、静默更新、事件同步等一系列功能,是 PWA 应用的核心技术之一。

与普通 JS 运行环境相比,Service Workers 有如下特点:

使用场景

Server Worker在PWA之外也有诸多应用,基于它对HTTP请求和响应的强大管理能力,它可以作为多种依赖网络的应用的核心流程管理器。

cache.addAll(urlsToPrefetch.map(function(urlToPrefetch) {
  return new Request(urlToPrefetch, { mode: 'no-cors' });
})).then(function() {
  console.log('All resources have been fetched and cached.');
}); 

30X的HTTP状态码尚不支持离线请求重定向, 这是一个已知的issue。建议在官方支持离线重定向前,根据你的使用场景寻找其他方案,
在使用Service Worker代理HTTP的响应体时,务必记住clone response,而不要直接消费掉响应体。 原因是HTTP response是一个 流, 它的内容只能被消费一次。 只要我们仍然希望既能让浏览器正确的获得响应体中的内容,又能是它被缓存或者在Service Worker作内容检查,请不要忘记复制一个响应体。

fetch(url, {
  credentials: 'include'
})

跨域资源默认是不支持缓存的,需要额外参数。

生命周期

其生命周期分为首次加载更新加载
首次访问页面时候Service Worker会立即被下载下来并进行尝试安装,安装成功后就会尝试去激活等操作
更新在默认情况下Service Worker一定会每24小时被下载一次,如果下载的文件是最新文件,那么它就会被重新注册和安装但不会被激活,当不再有页面使用旧的 Service Worker 的时候,它就会被激活。

用户首次访问service worker控制的网站或页面时,service worker会立刻被下载。
之后,在以下情况将会触发更新:
一个前往作用域内页面的导航
service worker 上的一个事件被触发并且过去 24 小时没有被下载
无论它与现有service worker不同(字节对比),还是第一次在页面或网站遇到service worker,如果下载的文件是新的,安装就会尝试进行。
如果这是首次启用service worker,页面会首先尝试安装,安装成功后它会被激活。
如果现有service worker已启用,新版本会在后台安装,但不会被激活,这个时序称为worker in waiting。直到所有已加载的页面不再使用旧的service worker才会激活新的service worker。只要页面不再依赖旧的service worker,新的service worker会被激活(成为active worker)。

首次加载
更新加载
页面加载完成注册
if('serviceWorker' in navigator) {
    window.addEventListener('load', function() {
       navigator.serviceWorker.register('sw.js', { scope: './' })
        .then(function (reg) {
          console.log('success', reg);
        })
        .catch(function (err) {
          console.log('fail', err);
        });
  });
}
安装
self.addEventListener('install', function (event) {
    event.waitUntil(
        caches.open(CACHE_NAME)
            .then(function(cache) {
                return cache.addAll(urlsToCache);
            })
    );
});
激活

一旦首次安装成功后或者sw进行更新就会触发activated相对首次安装会直接进入激活状态更新触发会显得比较复杂比如
A为老的sw
B为新的sw
B进入安装更新阶段时候A还在工作状态那么B就会进waiting阶段,只有等到A被terminated后,B才能完全替换A的工作

activate阶段可以做很多有意义的事情,比如更新存储在cache中的key和value,可以清理旧缓存和旧的service worker关联的东西

self.addEventListener('activate', function(event) {
  console.log('Service Worker activate');
    event.waitUntil(
        // 遍历 caches 里所有缓存的 keys 值
        caches.keys().then(function() {
          return caches.keys().then(function (keys) {
              var all = keys.map(function (key) {
                  if (key.indexOf(CACHE_NAME) !== -1){
                      console.log('[SW]: Delete cache:' + key);
                      return caches.delete(key);
                  }
              });
              return Promise.all(all);
          });
      })
    );
});
终止
terminated终止状态一般触发条件由下面几种方式:
拦截

fetch拦截阶段是sw最终要和关键阶段,主要用于拦截代理所有指定的请求,然后进行二次相应的处理操作通过这个阶段我们可以实现很多有意思的操作

输出缓存
self.addEventListener('fetch', function(event) {
    event.respondWith(
        caches.match(event.request)
            .then(function(response) {
                //该fetch请求已经缓存
                if (response) {
                    return response;
                }
                return fetch(event.request);
                }
            )
    );
});
输出JSON
self.addEventListener("fetch", event => {
  const data = {
    hello: "world"
  }

  const json = JSON.stringify(data, null, 2)

  return event.respondWith(
    new Response(json, {
      headers: {
        "content-type": "application/json;charset=UTF-8"
      }
    })
  )
})
输出HTML
const html = `<!DOCTYPE html>
<body>
  <h1>Hello World</h1>
  <p>This markup was generated by a Cloudflare Worker.</p>
</body>`

async function handleRequest(request) {
  return new Response(html, {
    headers: {
      "content-type": "text/html;charset=UTF-8",
    },
  })
}
addEventListener("fetch", event => {
  return event.respondWith(handleRequest(event.request))
})
重定向URL
const destinationURL = "https://www.baidu.com"
const statusCode = 301

async function handleRequest(request) {
  return Response.redirect(destinationURL, statusCode)
}

addEventListener("fetch", async event => {
  event.respondWith(handleRequest(event.request))
})
缓存一些静态文件
cons CACHE_NAME="my-site-v1"

self.addEventListener('install', function (event) {
  
  let url_list=[
      '/',
      '/static/xxx.css',
      '/static/xxx.js',
      'https://www.baidu.com/img/pc_629ee8886a9c20e7f3cb1d2889c3e45d.gif',
      '/static/xxx`.txt',
  ]; 
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(function(cache) {
        consloe.log("缓存打开成功");
        cache.addAll(url_list).then(function(){
            consloe.log("所有资源都已获取并缓存");
        });
      }).catch(function(error) {
          console.log('缓存打开失败:', error);
        })
  ); 
});

更多例子

上一篇 下一篇

猜你喜欢

热点阅读