美文网首页
浏览器缓存方案

浏览器缓存方案

作者: 指尖轻敲 | 来源:发表于2019-01-05 00:11 被阅读7次

    HTTP响应头的实现

    如果一个网站中的某个文件是百年不变的,那么肯定没有必要打开网页都重新获取啊。我们可以设置HTTP返回头的Cache-Control。

    • max-age=31536000:过期时间(单位是秒),最大不能超过一年,只要没过期或者手动清除缓存就会使用缓存。

    • no-cache:每次使用缓存之前,向服务器对资源进行验证。最终实现不使用缓存的过期资源的效果。(不要理解为字面意思:不缓存)

    • no-store:不使用缓存

    • must-revalidate:如果设置了max-age,在有效期内会使用缓存,否则就需要进行验证。所以可以结合使用Cache-Control: must-revalidate, max-age=60

    那么服务端怎么进行验证呢?服务器第一次返回的响应头中有一个ETag字段,该字段相当于一个标识,具体的生成逻辑由服务端决定。
    然后客户端之后的请求头中会携带一个If-None-Match字段,这个字段就是那个标识。服务器通过对比这个标识判断资源是否发生了更新。

    Expires VS max-age

    在响应头中有时还有一个expires字段,它和设置max-age一样都是设置有效期的。不过expires字段直接指定过期时间。而后者则是指定多长时间之后过期。

    expires: Mon, 24 Dec 2018 13:55:24 GMT
    
    last-modified VS Etag

    在响应头中还有一个字段last-modified,它也可以用来做验证。那么它与Etag有什么区别呢?可以说Etag应该是更为精准的,last-modified最多精确到秒,而Etag则是只要发生变化,标识就会改变。

    last-modified: Thu, 01 Jan 1970 00:00:00 GMT
    

    Service Workers

    1、Service Workers是什么?

    Service Workers是浏览器在后台独立于网页运行的脚本,它可以支持离线体验。有以下几点注意事项:

    • 它是一种JavaScript 工作线程,不能直接访问DOM

    • 服务工作线程是一种可编程网络代理,让您能够控制页面所发送网络请求的处理方式。

    • 它在不用时会被中止,并在下次有需要时重启

    • 在开发过程中,可以通过 localhost 使用服务工作线程,但如果要在网站上部署服务工作线程,需要在服务器上设置 HTTPS。

    2、生命周期
    1. 首先需要在我们的网页中注册Service Workers。注册会让浏览器后台执行Service Workers的安装步骤。

    2. 在Service Workers的独立脚本中,设置在安装过程中要缓存的文件,如果所有文件都缓存成功则安装成功;如果有任何一个失败都将失败,Service Workers就安装失败。

    3. 安装完成就该激活了,在这里我们对旧缓存进行管理。

    4. 激活之后,服务工作线程将会对其作用域内的所有页面实施控制

    接下来,我们详细了解一下各个步骤的代码写法:

    3、代码实现

    在页面中注册Service Workers,来启动安装。在register方法中告诉浏览器Service Workers脚本的路径,这里是在根目录下,所以Service Workers接受该域下所有的fetch事件。

    // 判断浏览器对Service Workers的支持情况
    if ('serviceWprker' in navigator) {
      window.addEventListener('load', () => {
        navigator.serviceWorker
          .register('/sw.js')
          .then(registration => {
            if (registration.waiting) {
              console.log('安装完毕');
            }
          })
          .catch(err => {
            console.log(err);
          });
      });
    }
    

    接下来看一下Service Workers中的安装过程,这里会监听一个install事件,在事件回调中又可以分为三部:

    var CACHE_NAME = 'my-cache';
    var cacheList = ['/', '/src/main.js'];
    self.addEventListener('install', event => {
      event.waitUntil(
        caches.open(CACHE_NAME).then(cache => {
          return cache.addAll(cacheList);
        })
      );
    });
    
    • 打开缓存,这里需要传入一个想要缓存的自定义名称。

    • 缓存文件,可以是一个由想要缓存的文件路径组成的数组

    • 确认所有需要的资产是否缓存,event.waitUntil() 方法带有 promise 参数并使用它来判断安装所花费的时间以及安装是否成功。

    在安装完成之后,我们可能会想要一个对fetch事件的处理,当监听到fetch请求时,先匹配缓存中的内容,如果有就返回缓存的内容,没有则发起网络请求。

    self.addEventListener('fetch', event => {
      event.respondWith(
        caches
          .match(event.request)
          .then(response => response || fetch(event.request))
      );
    });
    

    这里如果想要连续缓存新的请求,可以处理fetch请求的响应并将其添加到缓存中。

    self.addEventListener('fetch', event => {
      event.respondWith(
        caches.match(event.request).then(response => {
          if (response) {
            return response;
          }
          // 如果时新的请求就克隆一个请求
          var fetchRequest = event.request.clone();
          return fetch(fetchRequest).then(res => {
            // 校验响应,对第三方资产(type)的请求不会添加到缓存
            if (!res || res.status !== 200 || res.type !== 'basic') {
              return res;
            }
            // 该响应是Stream,所以主体只能用一次,需要克隆响应,一个发送给浏览器,一个保留在缓存中
            var responseToCache = res.clone();
            caches.open(CACHE_NAME).then(cache => {
              cache.put(event.request, responseToCache);
            });
            return res;
          });
        })
      );
    });
    

    接着看,当某个时候,Service Workers需要更新了,应该有以下步骤:

    • 在用户打开页面的时候浏览器会尝试在后台重新下载定义了Service Workers的脚本。如果有变化了就认为是更新了。

    • 这时新的Service Workers就会启动将触发install事件,此时此刻旧的Service Workers还在控制着页面,新的Service Workers处于waitting状态

    • 当前这个页面关闭之后,新的Service Workers就会占据控制权。

    • 新服务工作线程取得控制权后,就会触发其 activate 事件。

    在 activate 回调中一般就是进行缓存管理,把旧的Service Workers清除掉。先定义一个新的缓存百名单,然后遍历Service Workers所有的缓存,把不在白名单内的删除。

    self.addEventListener('activate', event => {
      // 白名单
      var cacheWhiteList = ['new-cache'];
      event.waitUntil(
        caches.keys().then(cacheNames => {
          return Promise.all(
            cacheNames.map(cacheName => {
              if (cacheWhiteList.indexOf(cacheName) === -1) {
                return caches.delete(cacheName);
              }
            })
          );
        })
      );
    });
    

    相关文章

      网友评论

          本文标题:浏览器缓存方案

          本文链接:https://www.haomeiwen.com/subject/jgelrqtx.html