http/2下仍需谨慎合并css:应按复用率和变更频率划分,基础样式可合并,路由/主题/实验样式应分离,并通过构建工具正确配置以避免缓存失效与调试问题。

合并 CSS 文件确实能提升性能,但前提是你的站点还在 HTTP/1.1 环境下运行,或者即使用了 HTTP/2,仍存在 TLS 握手、缓存粒度、磁盘 I/O 等隐性瓶颈。盲目合并反而会拖慢首屏或降低缓存复用率。
HTTP/1.1 下合并 CSS 为什么有效
浏览器对同一域名的并发 HTTP/1.1 请求有硬限制(通常是 6 个)。每个 <link rel="stylesheet" href="xxx.css"> 都占一个槽位,排队等待 TCP 连接、SSL 握手、响应返回——这些加起来可能比文件本身传输还耗时。
常见错误现象:reset.css、header.css、modal.css、footer.css 分开引入,Network 面板里看到它们串行加载,TTFB 和等待时间明显拉长。
- 每个请求都要重复发送 Cookie、User-Agent 等 header,带宽浪费不可忽略
- @import 在 CSS 内部再引入其他文件,会触发额外请求,且无法并行
- HTML 解析遇到第一个
<link>就暂停,直到该 CSS 下载+解析完成,阻塞渲染
HTTP/2 下合并还有没有意义
HTTP/2 支持多路复用,理论上多个 CSS 文件可并行传输,但现实没那么理想:
- TLS 握手仍需一次,小文件多意味着更多帧调度开销
- 服务端读取 10 个小文件,比读 1 个大文件更易触发磁盘寻道或冷缓存问题
- CDN 或反向代理对单个大文件的缓存命中率通常更高;而 10 个文件中只要一个更新,全部缓存失效
- 构建产物中若把
dark-theme.css和reset.css合并,用户切主题时就得重下整个包
所以 HTTP/2 不是“不用合并”的理由,而是“要更聪明地合并”——比如用 media 属性懒加载打印样式:<link rel="stylesheet" href="print.css" media="print">,它根本不会阻塞主页面渲染。
哪些 CSS 必须合并,哪些绝对不能动
判断依据不是“文件数量”,而是“是否在多数页面都用得上”和“变更频率是否一致”。
- 应该合并:
reset.css、utils.css、button.css、card.css—— 这些基础样式几乎每页都用,且极少改动 - 不应合并:
/admin/dashboard.css、ab-test-v2.css、prefers-reduced-motion.css—— 它们只在特定路由、实验分支或系统偏好下生效 - 可按需注入:
theme-dark.css用 JS 动态加载,或通过<link media="(prefers-color-scheme: dark)">声明式加载
一个容易被忽略的点:Webpack 的 MiniCssExtractPlugin 默认把 node_modules 里的 CSS 提取成 vendor.css,但它不会自动合并你 src 下的多个业务 CSS——这得靠 optimization.splitChunks.cacheGroups 显式配置,否则你以为合了,其实还是散的。
构建时合并 CSS 的实操要点
手动拼接 cat a.css b.css > all.css 看似简单,但会破坏 @import 路径、相对 URL、source map,且无法处理 CSS 变量作用域或 postcss 插件链。
- Vite 用户:默认已合并,但若发现 antd 样式和业务样式混在一起导致缓存失效率高,可在
build.rollupOptions.output.manualChunks中单独分组:antd: ["antd", "ant-design-vue"] - Webpack 用户:别只依赖
splitChunks.chunks: "all",要配合cacheGroups指定 CSS 归属,例如把所有src/styles/**/*归入app.css - PostCSS 流水线:用
postcss-import内联@import,再走cssnano压缩,适合无构建工具的轻量项目,但不支持动态路径重写
最常被跳过的一步:合并后必须验证 source map 是否可用,否则线上报错定位不到原始行号;另外,检查最终输出的 CSS 是否包含未使用的规则(比如某组件已下线,但其样式还躺在合并包里)——这比请求数更伤性能。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











