← 中文资源

简体中文版本

缓存清除 URL 构建器

为静态资源 URL 追加版本或内容哈希查询参数,同时保留已有查询参数与片段标识。

阅读英文原文

功能说明

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);
}