HTML中src和href路径出错的根本原因是浏览器严格按当前HTML文件位置及生效的<base>标签解析,而非开发者主观预期;部署到子路径或环境切换时易404,且<base>必须置于<head>最前、结尾带斜杠才有效。

HTML里写死的 src 和 href 路径为什么总出错
因为浏览器解析这些路径时,完全依赖当前 HTML 文件所在位置和 <base> 的实际生效状态,而不是你“以为”的位置。手写 ./js/app.js 或 /static/main.css 很容易在部署到子路径(如 /admin/)或切换开发/生产环境时 404。
-
<base href="/app/">必须放在<head>最顶部,否则前面的<link>、<script>已按原始路径解析完毕,<base>形同虚设 -
<base href="/subpath">少了个斜杠,Firefox 会截断成/subpathcss/,导致所有相对路径拼接错误 -
fetch('./api/data.json')、import('./utils.js')不受<base>影响——它只管 HTML 解析阶段的src/href属性 - 本地开发用
http://localhost:3000/作<base>值,比/更易区分后端路由冲突
Webpack/Vite 怎么自动注入带哈希的资源路径
构建工具不是“改 HTML”,而是把 index.html 当模板,在生成最终文件时,把 <script src="main.js"> 这类占位符替换成真实哈希路径,比如 <script src="main.f8494f81.js">。这一步必须由插件驱动,不能靠人眼核对。
- Webpack:用
HtmlWebpackPlugin+output.publicPath,确保publicPath与 CDN 或部署路径一致(如/assets/),插件才会重写所有<script>和<link> - Vite:设置
build.base = "/assets/",它会自动重写所有静态导入路径和public/下引用,但不会动你手写的<img src="logo.png">—— 所以这类路径也得走import或new URL(..., import.meta.url) - 禁止在 HTML 中写
src="./js/app.js":这种路径无法被构建工具识别为可替换目标,只会原样输出,哈希就丢了 - 如果用了多个 HTML 入口(如
about.html),每个都要单独配置插件处理,不能只靠一个index.html模板
HTML 本身不能加 contenthash 后缀,但必须配协商缓存
index.html 是入口文档,内容每次发布都可能变,给它加哈希后缀(比如 index.a1b2c3.html)是危险操作——所有相对路径失效、SEO 断链、PWA 清单指向旧文件、CDN 缓存找不到新入口。
- 正确做法:保持
/index.html路径固定,HTTP 响应头设为Cache-Control: no-cache, must-revalidate或max-age=0,再配合ETag或Last-Modified - JS/CSS/图片等静态资源才该加哈希,且响应头必须是
Cache-Control: public, max-age=31536000, immutable——immutable告诉浏览器“这个 URL 永远不变”,省掉If-None-Match请求 - CDN 缓存规则要匹配哈希路径,例如正则
/*.f[0-9a-f]{8}.*\.(js|css|png)/,漏配会导致用户一直加载旧资源 -
localStorage存哈希做增量校验?只能辅助,不能替代 HTTP 缓存——首次访问没指纹、哈希碰撞、服务端文件被覆盖但哈希未更新,都会出问题
动态跳转版本(如 /v2.1/index.html → /v2.2/index.html)怎么安全实现
靠 <base> 或 HTML 标签做不到自动升级,必须由 JS 主动判断并跳转。硬编码 <base href="/v2.1/"> 升级时忘了改,用户就永远卡在旧版。
立即学习“前端免费学习笔记(深入)”;
- 从
location.pathname提取当前版本段,比如用正则/^\/(v\d+\.\d+)/匹配 - 用
fetch("/v2.2/index.html", { method: "HEAD" })预检目标页面是否存在且可访问,只在返回200时才跳转 - 跳转前清除可能污染的旧状态:
localStorage.removeItem("app-state")、caches.delete("old-cache") - 禁用
<meta http-equiv="refresh">:它不支持条件判断,强制刷新会造成白屏,且无法控制缓存清理
build.base 但 CDN 没配哈希缓存规则,或者 Webpack 注入了哈希却忘了关 HTML 的强缓存,用户看到的就还是旧页面。



















