给静态资源加版本号,修掉「服务端更新了但界面没变」
现象:CSS 更新并上线后,用户打开站点看不到任何变化,怀疑没部署。
实测服务器上的 style.css 已是新版(与工作树逐字节一致),真实浏览器
直连线上也拿得到新文件,所以问题在客户端缓存:
· 页面 HTML 带 no-store,每次都重新渲染 → HTML 一直是新的;
· 但 /assets/*.css|js 的响应没有 Cache-Control / Expires,浏览器只好
退回自己的「启发式缓存」——在它自行估算的新鲜期内根本不发请求;
· 而 <link href="/assets/style.css"> 的地址从不改变,文件换了浏览器
也无从得知。于是出现「HTML 新的、CSS 旧的」,界面看起来毫无变化。
修法:新增 asset() 辅助函数,拼上 mtime + 文件大小做 cache busting,
文件名不变而内容一变、地址就变,浏览器只能重新拉取,普通刷新即生效。
版本号同时带上文件大小,挡住「mtime 没变但内容变了」这种漏网情况;
取不到文件信息时不加版本号,退化成原行为,不影响可用性。
已验证:线上 HTML 现在引用 /assets/style.css?v=…;真实浏览器直连线上
三个视口(1920/1280/390)均无横向溢出,资源全部 200;1920px 下顶栏品牌
与正文左边缘同为 420px,头像 26×26(源图 400×400)。
This commit is contained in:
@@ -25,6 +25,29 @@ function url(string $route, array $params = []): string
|
|||||||
return '/index.php?' . http_build_query($params);
|
return '/index.php?' . http_build_query($params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 静态资源地址 + 内容版本号(cache busting)。
|
||||||
|
*
|
||||||
|
* 为什么必须有:CSS/JS 的响应没有 Cache-Control/Expires,浏览器会退回到自己的
|
||||||
|
* 「启发式缓存」——在它自行估算的新鲜期内**根本不发请求**。文件换了它也不知道,
|
||||||
|
* 因为地址一直没变。表现就是「服务端明明更新了,用户看到的还是旧样式」。
|
||||||
|
* 拼上随文件变化的版本号后,文件一改地址就变,浏览器只能重新拉。
|
||||||
|
*
|
||||||
|
* 版本号用 mtime + 文件大小:只用 mtime 时,同一秒内改两次会漏;
|
||||||
|
* 补上大小能挡住绝大多数「内容变了但 mtime 没变」的情况。
|
||||||
|
* 取不到文件信息就不加版本号,退化成原来的行为 —— 不影响可用性。
|
||||||
|
*/
|
||||||
|
function asset(string $path): string
|
||||||
|
{
|
||||||
|
$path = '/' . ltrim($path, '/');
|
||||||
|
$file = dirname(__DIR__) . $path;
|
||||||
|
$mtime = @filemtime($file);
|
||||||
|
if ($mtime === false) {
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
|
return $path . '?v=' . $mtime . '-' . (int)@filesize($file);
|
||||||
|
}
|
||||||
|
|
||||||
function redirect(string $route, array $params = [], string $anchor = ''): void
|
function redirect(string $route, array $params = [], string $anchor = ''): void
|
||||||
{
|
{
|
||||||
$location = url($route, $params);
|
$location = url($route, $params);
|
||||||
|
|||||||
@@ -15,9 +15,10 @@ $__initial = $__name !== '' ? mb_substr($__name, 0, 1) : '·';
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||||
<title><?= e($pageTitle ?? '家庭出行记录') ?></title>
|
<title><?= e($pageTitle ?? '家庭出行记录') ?></title>
|
||||||
<link rel="stylesheet" href="/assets/style.css">
|
<?php /* 静态资源一律走 asset():不带版本号的话,浏览器会一直用缓存里的旧 CSS */ ?>
|
||||||
<link rel="stylesheet" href="/assets/vendor/cropper/cropper.min.css">
|
<link rel="stylesheet" href="<?= e(asset('assets/style.css')) ?>">
|
||||||
<script src="/assets/vendor/cropper/cropper.min.js"></script>
|
<link rel="stylesheet" href="<?= e(asset('assets/vendor/cropper/cropper.min.css')) ?>">
|
||||||
|
<script src="<?= e(asset('assets/vendor/cropper/cropper.min.js')) ?>"></script>
|
||||||
<script>
|
<script>
|
||||||
window._AMapSecurityConfig = {
|
window._AMapSecurityConfig = {
|
||||||
securityJsCode: '<?= e(app_config()['amap']['security_js'] ?? '') ?>',
|
securityJsCode: '<?= e(app_config()['amap']['security_js'] ?? '') ?>',
|
||||||
|
|||||||
Reference in New Issue
Block a user