缓存失效、性能和网络
人们普遍认为,编程中最难做的事情之一就是缓存失效。然而,对于资产(js 文件、css 文件、图像等)来说,提供 Web 资产并不是真正的最佳逻辑:
然而,当应用于网络时,有一个复杂的问题。
考虑对 的请求/css/main.css
,其中包含:
body {
background-image:url('../img/bar.gif');
}
显然,这将触发对/img/bar.gif
何时加载 css 文件的请求。假设图像带有适当的标头,则只有两种方法可以加载 的更新版本bar.gif
:
第一个是有问题的,如果它不是自动化的(即使是自动化的,也可能出错),第二个很容易。
版本前缀资产 url -> 再也不会有问题
解决 css/js/files 问题的一种简单方法是使您的内部版本号成为 url 的一部分:
/v123/css/foo.css
^
您可以通过修改您的应用程序助手 webroot 功能来做到这一点,例如:
public function webroot($file) {
$file = parent::webroot($file);
if (Configure::read('debug')) {
return $file;
}
return '/' . Configure::read('App.version') . $file;
}
顺便说一句,使用 cdn 是相同的技术——这可能是提高前端性能所能做的最好的事情。
通过这种方式,当您提升您的网站版本时 - 所有资产都会获得新的网址。请注意,使用此技术,所有引用的资产都需要使用相对 url,而不是绝对:
.foo {
/* background-image:url('/img/bar.gif'); // won't work */
background-image:url('../img/bar.gif');
}
否则,对 css 文件的请求是特定于应用程序版本的,但引用的图像不是,即使使用新的应用程序版本,也会从浏览器缓存(如果相关)中读取。
实现相同的结果,不更改文件系统
如果您不想更改文件夹结构,可以使用类似于 h5bp 中的重写规则进行文件名缓存清除:
<IfModule mod_rewrite.c>
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^/v\d+/(css|files|js)/(.+)$ /$1/$2 [L]
</IfModule>
这将意味着 url在请求时/v123/css/main.css
提供内容。/css/main.css
解析css文件很复杂
你在评论中提到
我认为更改一项资产会导致重新下载所有资产的事实是一个交易破坏者
除非您每分钟都发布一个新的生产版本 - 这不会成为问题(除非您有 GB 的缓存内容,在这种情况下......您有不同的问题)。拥有特定于文件的高性能缓存逻辑的唯一方法是例如将站点中的每个文件存储为文件内容的 sha1 - 应用于 css 文件,这意味着替换../img/foo.gif
为../img/<hash of foo.gif's contents>.gif
.
没有什么可以停止使用多种技术,例如使用以下结构:
app
webroot
css
img <- css assets only
fonts
img
js
您可以为您的 css、字体和 js 请求添加版本前缀;间接地对 css-images 执行相同的操作(假设它们使用表单的相对 url background-image:url('img/bar.gif');
),而不将相同的逻辑应用于其他资产(用户头像、他们上传的猫视频等)。
或者对所有图像使用数据 uri
这就是谷歌所做的=)。
归根结底,您需要在构建过程的复杂程度和真正的好处之间做出选择。许多用户的浏览器缓存为空,因此对于随机用户而言,应用程序的缓存逻辑很可能仅适用于他们当前的访问——一次性使整个资产缓存过期的主要原因之一并不是那么糟糕事物。