天天看点

【高】HTTP 缓存策略你真的有了解吗?

作者:成都程序员晴小篆

HTTP缓存策略是指浏览器和服务器之间在传输资源时,如何使用缓存的方式。HTTP缓存的主要目的是减少网络传输的数据量,提高页面的访问速度。

缓存的主要策略有哪些?

HTTP缓存策略主要包括以下几种:

  • 强缓存:通过设置 HTTP 头部中的 Expires 或 Cache-Control 字段来指定资源在本地缓存的有效期。当资源未过期时,浏览器直接从缓存中读取,不会向服务器发送请求,从而提高页面的访问速度。
  • 协商缓存:当资源的缓存时间已经过期,浏览器会向服务器发送请求,服务器会检查资源是否有更新,如果没有更新,则返回 304 状态码,告诉浏览器直接使用本地缓存。
    • Last-Modified / If-Modified-Since:服务器在返回资源时,会添加 Last-Modified 头部字段,表示资源最后的修改时间。当浏览器下次请求该资源时,会在请求头部添加 If-Modified-Since 字段,表示上次请求时资源的修改时间。服务器检查这两个时间是否一致,如果一致,则返回 304 状态码,否则返回新的资源。
    • ETag / If-None-Match:服务器在返回资源时,会添加 ETag 头部字段,表示资源的唯一标识。当浏览器下次请求该资源时,会在请求头部添加 If-None-Match 字段,表示上次请求时资源的唯一标识。服务器检查这两个标识是否一致,如果一致,则返回 304 状态码,否则返回新的资源。
  • 离线缓存:通过使用 HTML5 提供的 Application Cache API,可以将页面的资源缓存在本地,使得用户在没有网络连接的情况下也能够访问页面。
  • Service Worker 缓存:Service Worker 是一种在浏览器后台运行的 JavaScript 线程,可以拦截和处理浏览器发送的网络请求。通过使用 Service Worker,可以将页面的资源缓存在本地,提高页面的访问速度和用户体验。

强缓存中 Expires 或 Cache-Control 有什么区别?

在 HTTP 缓存策略中,强缓存是指在一定时间内,直接使用本地缓存而不发送请求到服务器。Expires 和 Cache-Control 是用于设置强缓存的两种方式。

  • Expires: 是 HTTP/1 的产物,它是一个 HTTP 头字段,表示资源过期时间,是一个绝对时间。服务器返回的 HTTP 头中,如果包含 Expires 字段,则表示该资源在该过期时间之前可以直接从缓存中获取,而不需要再次请求服务器。
  • Cache-Control: 是 HTTP/1.1 的产物,是一个 HTTP 头字段,用来控制文档缓存行为。它的值可以是很多不同的指令,例如 max-age、no-cache、no-store、must-revalidate 等等。其中,max-age 指令可以设置资源的最大有效时间,单位是秒。如果服务器返回的 HTTP 头中包含 Cache-Control 指令,则浏览器会根据该指令的值来决定是否直接使用本地缓存,而不需要再次请求服务器。

Expires 是一个绝对时间,因此它的缺点是当服务器的时间与客户端的时间不一致时,缓存过期时间就可能会出现偏差。

而 Cache-Control 是一个相对时间,因此它的缺点是需要服务器和客户端的时间保持一致,同时需要正确设置 max-age 的值。

在实际应用中,建议使用 Cache-Control,因为它更加灵活和可控。

离线缓存 Application Cache API 是如何缓存 http 资源的?

Application Cache API(应用程序缓存)是 HTML5 标准中提供的一个用于离线缓存 Web 应用程序的技术。它可以将 Web 应用程序中的文件(包括 HTML、CSS、JavaScript 和图像等)保存到客户端浏览器中的缓存中,在没有网络连接的情况下,仍然能够访问应用程序。

在 Application Cache API 中,通过在 cache manifest 文件中列出需要缓存的资源列表来实现离线缓存。该文件必须以 .appcache 为后缀名,并且必须在 Web 服务器上进行访问。浏览器会下载该文件,并将文件中列出的资源文件下载到本地缓存中。当应用程序在离线状态下打开时,浏览器会自动从本地缓存中加载缓存的文件。

下面是一个简单的 cache manifest 文件示例:

CACHE MANIFEST
# version 1.0.0

CACHE:
index.html
styles.css
script.js
image.jpg

NETWORK:
*

FALLBACK:
           

上面的示例文件将缓存 index.html、styles.css、script.js 和 image.jpg 等资源文件,同时指定 NETWORK 和 FALLBACK,这两个属性分别用于指定离线缓存不生效时的网络连接策略和替换资源文件。

需要注意的是,Application Cache API 并不是一种完美的缓存技术,它也存在一些缺陷。例如,当更新 Web 应用程序时,需要手动清除客户端浏览器中的缓存才能生效,否则用户访问的仍然是旧版本的应用程序。同时,Application Cache API 只能缓存 GET 请求,不支持 POST 等其他请求方法。因此,为了更好地实现离线缓存,可以使用其他技术,例如 Service Worker。

Service Worker 是如何缓存 http 请求资源的?

ervice Worker 是一种在浏览器后台运行的脚本,可以拦截和处理浏览器网络请求。因此,可以使用 Service Worker 来缓存 http 请求资源。

Service Worker 可以通过以下步骤来缓存 http 请求资源:

  1. 注册 Service Worker:通过在页面中注册 Service Worker,可以告诉浏览器使用 Service Worker 来处理网络请求。
  2. 安装 Service Worker:一旦 Service Worker 被注册,浏览器就会下载并安装它。在安装过程中,Service Worker 可以缓存一些静态资源(如 HTML、CSS 和 JavaScript 文件)。
  3. 激活 Service Worker:一旦 Service Worker 安装成功,它就可以被激活。在激活过程中,Service Worker 可以删除旧版本的缓存,或者执行其他一些操作。
  4. 拦截网络请求:一旦 Service Worker 被激活,它就可以拦截浏览器发送的网络请求。
  5. 处理网络请求:当 Service Worker 拦截到网络请求时,它可以执行一些自定义的逻辑来处理这些请求。例如,它可以检查缓存中是否已经存在该请求的响应,如果存在,则直接返回缓存中的响应,否则,它可以将请求发送到服务器并缓存服务器的响应。
  6. 更新缓存:如果缓存中的资源发生了变化,Service Worker 可以自动更新缓存。例如,它可以在后台下载最新的资源,并更新缓存中的文件。

需要注意的是,使用 Service Worker 来缓存 http 请求资源需要一些额外的工作。例如,需要编写 Service Worker 脚本来处理请求,并且需要将该脚本注册到浏览器中。此外,还需要考虑一些缓存策略,以确保缓存的数据与服务器上的数据保持同步。

下面是一个使用 Service Worker 实现缓存的示例代码:

// 注册 Service Worker
if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('/service-worker.js').then(function(registration) {
      console.log('ServiceWorker registration successful with scope: ', registration.scope);
    }, function(err) {
      console.log('ServiceWorker registration failed: ', err);
    });
  });
}

// 安装 Service Worker
self.addEventListener('install', function(event) {
  console.log('ServiceWorker install');
  event.waitUntil(
    caches.open('my-cache').then(function(cache) {
      return cache.addAll([
        '/',
        '/index.html',
        '/styles.css',
        '/script.js',
        '/image.png'
      ]);
    })
  );
});

// 激活 Service Worker
self.addEventListener('activate', function(event) {
  console.log('ServiceWorker activate');
});

// 拦截网络请求
self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request).then(function(response) {
      if (response) {
        console.log('ServiceWorker fetch from cache:', event.request.url);
        return response;
      } else {
        console.log('ServiceWorker fetch from network:', event.request.url);
        return fetch(event.request);
      }
    })
  );
});

// 更新缓存
self.addEventListener('activate', event => {
  event.waitUntil(
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames.filter(cacheName => {
          return cacheName.startsWith('my-cache') &&
            cacheName !== 'my-cache';
        }).map(cacheName => {
          return caches.delete(cacheName);
        })
      );
    })
  );
});           

当网络请求到来时,会首先在缓存中查找对应的资源,如果有则直接返回缓存中的资源,否则从网络中获取资源并返回。这样就可以实现基本的离线缓存功能。

在这个示例中,当 Service Worker 被安装时,我们打开一个新的缓存并将应用程序的静态资源添加到缓存中。在 fetch 事件中,我们拦截每个网络请求并尝试匹配它们到我们的缓存中,如果匹配到了则返回缓存的响应,否则通过 fetch 方法从网络中获取资源。在 activate 事件中,我们可以更新缓存,删除旧的缓存项并将新的缓存项添加到缓存中。

继续阅读