功能说明
withCacheBusting 为脚本、样式、图片等静态资源 URL 追加版本参数,使发布新版本后浏览器与 CDN 能获取新文件。函数保留原有的查询参数和 hash 片段,并支持自定义参数名。hashAssetContent 用 FNV-1a 把构建产物内容转换为短哈希,可作为版本值,避免手工维护版本号。相对路径与绝对 URL 都能处理。
关键代码
函数用固定占位域名作为 URL 解析基准,相对路径解析后如果 origin 仍是占位域名,就只返回路径、查询与片段,保持相对形式;绝对 URL 则返回完整字符串。参数名先用正则校验为合法标识符,版本值经 String 转换后交给 URLSearchParams,自动完成必要的百分号编码。哈希函数逐字符异或并乘以 FNV 质数,最后无符号化并转成三十六进制。
使用注意
查询参数缓存清除依赖服务器与 CDN 正确配置,部分 CDN 忽略查询字符串,此时应改用文件名哈希。修改参数会改变缓存键,频繁变化的版本值会降低缓存命中率,建议使用内容哈希而不是时间戳。不要对包含敏感信息的 URL 使用,参数会出现在日志与 Referer 中。哈希只用于版本标识,不具备抗碰撞安全性。
验证方式
测试无查询、已有查询、带片段、相对路径和绝对 URL 五种输入,确认参数被正确追加且其他部分不变。验证同名参数会被覆盖而不是重复。测试非法参数名、空 URL 与非字符串输入抛错。哈希函数测试相同内容稳定、不同内容大概率不同、空字符串有确定值,并验证输出只含小写字母与数字。
代码示例
// Cache-busting URL builder for static assets.
export function withCacheBusting(url, version, param = "v") {
if (typeof url !== "string" || url.length === 0) {
throw new TypeError("url must be a non-empty string");
}
if (!/^[A-Za-z_][A-Za-z0-9_-]*$/.test(param)) {
throw new TypeError("param must be a valid query parameter name");
}
const base = "https://cache-busting.invalid";
const parsed = new URL(url, base);
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
throw new TypeError("only http(s) URLs are supported");
}
parsed.searchParams.set(param, String(version));
if (parsed.origin === base) {
return parsed.pathname + parsed.search + parsed.hash;
}
return parsed.toString();
}
export function hashAssetContent(content) {
let hash = 0x811c9dc5;
const text = String(content);
for (let index = 0; index < text.length; index += 1) {
hash ^= text.charCodeAt(index);
hash = Math.imul(hash, 0x01000193);
}
return (hash >>> 0).toString(36);
}