告诉浏览器缓存HTML内容,但让它们立即接收更新



假设我们正在构建一个静态网站,比如一个博客。我们希望最大限度地利用服务器和读者之间的缓存层;也许服务器运行在作者的家庭网络上,他们想要充分利用Cloudflare的缓存。但我们也希望允许作者在闲暇时修改网站,让读者立即接受这些变化。

对于从HTML文档引用的外部资源,比如样式表,实现这一点的典型方法是从与样式表的当前版本相对应的端点提供样式表,服务器指示客户端缓存该样式表,然后从包含该样式表的任何HTML文档链接到该端点。如果修改了样式表,则从不同的端点提供新版本,并更改所有HTML文档以引用新端点。

这对于外部资源来说是很好的,但是我们如何缓存HTML文档本身呢?

方法1:使用重定向

为每个版本的HTML文档提供一个唯一的端点,可能是内容寻址的,并响应文档规范路径的请求,使用302 Found重定向到当前版本。

问题:读者可以在他们的地址栏中看到当前版本页面的url。这个url很可能是机器生成的,而且很难看,更重要的是,如果他们复制url并链接到其他地方,那么链接到当前版本的页面可能已经过时,甚至将来不可用。

方法1.1:使用window.history.replaceState()

与上同,但使用"当前版本"端点包含一个脚本,该脚本使用window.history.replaceState()将浏览器地址栏中显示的内容更改为页面的规范路径。

问题:与上面相同的问题,但仅适用于禁用javascript的用户。此外,它的奇怪行为可能会导致混乱,并可能破坏使用历史API的页面(对于博客来说很奇怪,但我想找到最好的通用方法)。

方法2:用javascript加载内容在链接到任何外部资源的页面规范端点处提供一个基本HTML文档,然后动态加载内容。指示客户端不要缓存此页面。内容的每个版本都从一个唯一的端点提供,并指示客户端缓存它。使用fetch流接口和document.write应该达到与让浏览器加载静态页面相似的性能。

问题:禁用javascript的用户无法看到内容。他们可能会看到一个指向当前版本内容的链接,但是遵循该链接的用户会遇到上面重定向部分中描述的相同问题。

方法3:<iframe>,<object>

嵌入规范页面的内容。通常,内容托管在特定于版本的端点上,有缓存的指令,规范页面有不缓存的指令。

问题:链接会影响嵌套的浏览上下文,不会改变地址栏中显示的内容。

我对这个答案并不完全满意,但我认为这是在撰写本文时一般情况下可用的最佳解决方案。动态加载内容(方法2)在某些情况下可能更好,比如由于其他原因需要使用javascript。如果我是对的,没有更好的方法,那是不可接受的。这种风格的缓存非常有用,不应该使用这种笨拙的方法。

如果我错过了更好的东西,请与其他方法配合,即使它只在有限的上下文中更好!

使用方法3中的<object><iframe>,并在所有包含的<a>标签上设置target="_parent"

使用target="_parent"而不是target="_top"是很重要的,这样如果第三方在iframe中嵌入规范页面,点击链接不会逃离iframe,导致用户的浏览器从第三方的页面导航出去。

例子:

文件:page1.html

<!doctype html>
<html lang=en>
<head>
<meta charset=utf-8>
<title>Page 1</title>
<style>
html, body {
margin: 0;
padding: 0;
overflow: hidden;
height: 100%;
}
#include {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<object id="include" type="text/html" data="/blob/36a333faa933f67db4fef10f03c9bc92dc85b9ee7b
7ae5fc82ae706440fe0cfa.html"></object>
</body>
</html>

文件:page2.html

<!doctype html>
<html lang=en>
<head>
<meta charset=utf-8>
<title>Page 2</title>
<style>
html, body {
margin: 0;
padding: 0;
overflow: hidden;
height: 100%;
}
#include {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<object id="include" type="text/html" data="/blob/d5995a8f5f41837f97a53acdb942a41a843cc5
c0801ea7d2842643d90d0fe924.html"></object>
</body>
</html>

文件:blob/36 a333faa933f67db4fef10f03c9bc92dc85b9ee7b7ae5fc82ae706440fe0cfa.html

<!doctype html>
<html lang=en>
<head>
<meta charset=utf-8>
<title>Page 1 <!-- this will never be displayed --></title>
</head>
<body>
<p>Page 1</p>
<p><a target="_parent" href="/page2.html">Go to page 2</a></p>
</body>
</html>

文件:blob/d5995a8f5f41837f97a53acdb942a41a843cc5c0801ea7d2842643d90d0fe924.html

<!doctype html>
<html lang=en>
<head>
<meta charset=utf-8>
<title>Page 2 <!-- this will never be displayed --></title>
</head>
<body>
<p>Page 2</p>
<p><a target="_parent" href="/page1.html">Go to page 1</a></p>
</body>
</html>

最新更新