如果文件由 service worker 管理,则绕过默认缓存策略

Bypass default caching policy if file is managed by service worker

我正在使用 Google 的 workbox-cli 工具来预缓存我网站上的一些文件。是否可以将网络服务器设置为 return 默认情况下所有文件的 HTTP 响应 header 中的以下内容:

cache-control: s-maxage=2592000, max-age=86400, must-revalidate, no-transform, public

但是,只有当服务工作者要预缓存文件时,让网络浏览器使用以下内容:

cache-control: s-maxage=2592000, max-age=0, must-revalidate, no-transform, public

所以,我希望 service worker 在预缓存文件之前将网络服务器的响应 header 中的 max-age=86400 更改为 max-age=0。这使得服务工作者从网络服务器获取文件,这些文件根据 sw.js 中的修订而更改,而不是从本地缓存中检索它们。默认情况下,任何不由 Service Worker 管理的文件都会缓存 86400 秒。

一些背景信息

目前,我正在使用以下 bash 脚本来设置我的 sw.js:

#!/bin/bash
if [ ! -d /tmp/workbox-configuration ]; then
mkdir /tmp/workbox-configuration
fi
cat <<EOF > /tmp/workbox-configuration/workbox-config.js
module.exports = {
  "globDirectory": "harp_output/",
  "globPatterns": [
EOF
( cd harp_output && find assets de en -type f ! -name "map.js" ! -name "map.json" ! -name "markerclusterer.js" ! -name "modal.js" ! -name "modal-map.html" ! -name "service-worker-registration.js" ! -name "sw-registration.js" ! -path "assets/fonts/*" ! -path "assets/img/*-1x.*" ! -path "assets/img/*-2x.*" ! -path "assets/img/*-3x.*" ! -path "assets/img/maps/*" ! -path "assets/img/video/*_1x1.*" ! -path "assets/img/video/*_4x3.*" ! -path "assets/js/workbox-*" ! -path "assets/videos/*" ! -path "de/4*" ! -path "de/5*" ! -path "en/4*" ! -path "en/5*" | sort | sed 's/^/"/' | sed 's/$/"/' | sed -e '$ ! s/$/,/' >> /tmp/workbox-configuration/workbox-config.js )
cat <<EOF >> /tmp/workbox-configuration/workbox-config.js
  ],
  "swDest": "/tmp/workbox-configuration/sw.js"
};
EOF
workbox generateSW /tmp/workbox-configuration/workbox-config.js
sed -i 's#^importScripts(.*);$#importScripts("/assets/js/workbox-sw.js");\nworkbox.setConfig({modulePathPrefix: "/assets/js/"});#' /tmp/workbox-configuration/sw.js
sed -i 's/index.html"/"/' /tmp/workbox-configuration/sw.js
uglifyjs /tmp/workbox-configuration/sw.js -c -m -o harp_output/sw.js

在我的 Nginx 网络服务器上,默认传送以下 HTTP header:

more_set_headers "cache-control: s-maxage=2592000, max-age=0, must-revalidate, no-transform, public";

但是,如果请求的资源没有被 service worker 处理,默认的 cache-control 设置将被覆盖:

location ~ ^/(assets/(data/|fonts/|img/(.*-(1|2|3)x\.|maps/|video/.*_(1x1|4x3)\.)|js/(map|markerclusterer|modal|service-worker-registration|sw-registration)\.js|videos/)|(de|en)/((4|5).*|modal-map\.html)) {
    more_set_headers "cache-control: s-maxage=2592000, max-age=86400, must-revalidate, no-transform, public";
}

当前方法存在问题(参见背景信息)

  1. 我必须跟踪文件并相应地更新 nginx.conf
  2. max-age=0 也用于不支持 service-workers 的网络浏览器。因此,他们在每次访问页面时都从网络服务器请求资源。

第一次更新

我想要的预缓存行为可以用 workbox strategies 中的两个来说明。我希望 service worker 显示场景 1 和场景 2 中描述的以下行为,尽管 cache-control: max-age=86400 是由网络服务器在 HTTP header 中为资产(例如 default.js)提供的。

情况 1:sw.js 中的修订没有更改

访问网页,由于 max-age=0 从网络服务器检索 sw.js 文件,网络浏览器注意到 default.js 的修订没有改变。在这种情况下,default.js 从预缓存缓存中检索:

场景 2:sw.js 中的修订确实发生了变化

访问网页,由于 max-age=0 从网络服务器检索 sw.js 文件,网络浏览器注意到 default.js 的修订已更改。在这种情况下,default.js 是从网络服务器检索到的:

第二次更新

基本上,所需的策略类似于 network-first strategy。但是,仅当 sw.js 中的文件修订已更改时才执行第 2 步。

第三次更新

如果我没记错的话,这个上面已经有some work了:

self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(`static-${version}`)
      .then(cache => cache.addAll([
        new Request('/styles.css', { cache: 'no-cache' }),
        new Request('/script.js', { cache: 'no-cache' })
      ]))
  );
});

我认为您对 Service Worker 的实际工作方式了解不够全面。

您定义一个或多个缓存供服务工作者使用。您指定缓存中的内容,是否缓存未来的请求等

Service Worker 现在会拦截来自客户端的所有网络请求,然后按照您编程的方式响应它们。它可以 return 缓存内容(如果可用),在通过网络更新时先缓存内容,在没有连接的情况下先网络并复制到缓存,缓存图像但不缓存其他任何内容,仅缓存 GET 请求,仅缓存某些域、文件类型等......

它缓存什么以及每个缓存的有效时间完全取决于您,完全不受服务器响应的影响 headers。如果你告诉你的 service worker 对资源发出获取请求,那么它会通过网络加载该资源,而不管任何 headers 或已经在本地缓存的内容。

您可以完全控制整个缓存过程,这非常有用,但也有一些缺陷。

我在 cache-control HTTP header 中使用 s-max-age 而不是 s-maxage,这导致我的反向代理和工作箱服务工作者出现一些意外行为。修复后,Service Worker 按预期工作。