html模板必须设为no-cache或max-age=0,因动态路由入口或ssr起点不可长期缓存;静态资源需文件名哈希+长期缓存;模板结构影响解析性能,阻塞脚本、编码声明位置、第三方脚本顺序及服务端压缩均关键。

HTML模板本身不能被长期缓存
HTML 文件必须设为 no-cache 或 max-age=0, must-revalidate,哪怕你用构建工具生成了带哈希的文件名(比如 index.a1b2c3.html),只要它是动态路由入口或 SSR 渲染起点,就绝不能走 public, max-age=31536000。否则用户看到的永远是旧 HTML——按钮失效、文案没更新、JS 路径 404。
-
浏览器只认 URL,URL 不变,
max-age再长也只会复用旧内容 -
no-cache表示每次请求都需向服务端验证(发If-None-Match),返回304 Not Modified才复用本地副本,兼顾新鲜性与传输节省 -
meta http-equiv="Cache-Control"在 Chrome/Firefox/Edge 中完全无效,仅某些老旧 WebView 可能识别,不能当真
静态资源必须靠文件名哈希 + 长期缓存
真正影响首屏速度的是 HTML 引用的 JS/CSS/图片,它们必须和 HTML 的缓存策略解耦。
- JS/CSS 输出时启用构建工具的
contenthash(Webpack/Vite/Rollup 默认支持),生成类似main.ea7f2d.js的文件名 - 服务端对这类资源响应头设为
Cache-Control: public, max-age=31536000 - 图片若由 CMS 上传、无法自动加哈希,则依赖
ETag或Last-Modified协商缓存,Nginx 默认开启etag on,但需确认未被 CDN 层覆盖
HTML 模板结构直接影响解析性能
浏览器是流式解析 HTML 的,任何阻塞行为都会中断后续标签处理,模板写法比“有没有缓存”更早决定首屏是否卡顿。
-
<script></script>标签放在里且没加defer或async,会立即下载执行,暂停整个 DOM 构建 - 内联大段 JS(如埋点配置、初始化对象)超过 1KB 就该外链,否则 parser blocking 时间明显拉长
-
<meta charset>必须出现在 HTML 第一行,否则编码识别延迟可能触发整页重解析 - 避免在开头堆砌多个第三方
<script></script>,它们串行阻塞,尤其在弱网环境下放大 TTFB
服务端压缩比客户端“精简”更关键
HTML 体积小,但 gzip/Brotli 压缩后仍能缩小 50%~70%,且无需改动模板结构;而手动删空格、合并换行极易破坏语义或触发兼容问题。
- 启用 Brotli(优先)或 Gzip,由 Nginx/Apache/Node.js 后端统一开启,不是前端构建阶段的事
- 不要用
html-minifier开removeEmptyAttributes,它会误删<table> 必需属性,导致渲染异常<li>删除开发残留:注释 <code><!-- TODO: remove before prod -->、调试用<div id="debug-overlay"> 等,它们在生产环境毫无价值<p>真实瓶颈常藏在 HTML 解析阶段——缓存策略只是让“正确的内容更快到达”,而模板结构决定了“内容能不能被快速消化”。一个没加 <code>defer的统计脚本,比没配好Cache-Control更早让用户看到白屏。











