关键CSS必须内联,其余延迟加载;用Coverage面板提取critical.css(≤14KB)并内联,大体积CSS用preload+onload注入(需置空onload句柄)并配noscript回退;按变更频率拆分CSS、关闭Vite cssCodeSplit、配齐CDN缓存头。

关键CSS必须内联,其余延迟加载
低网速下,浏览器遇到第一个 <link rel="stylesheet"> 就会暂停 HTML 解析,等 CSSOM 构建完才继续。哪怕只有 10KB 的 CSS,如果它排在 200KB 的 main.css 前面,整个首屏渲染就被卡住。所以不能只看“体积大不大”,要看“谁阻塞谁”。
实操建议:
- 用 Chrome DevTools 的 Coverage 面板(
Cmd+Shift+P→ “Coverage”)跑一遍首屏操作,找出真正被用到的 CSS 规则,提取为critical.css - 把
critical.css内联进 HTML 的<head>中,注意要加type="text/css"且避免过长(建议 ≤ 14KB,否则可能触发 iOS Safari 的内联样式解析截断) - 剩余大体积 CSS(如
vendor.css、dashboard.css)全部移出<head>,改用<link rel="preload">+ JS 注入方式加载
preload + onload 注入必须加防重触发
直接写 <link rel="preload" href="big.css" as="style" onload="this.rel='stylesheet'"> 在 Safari 上会反复触发 onload,导致样式重复应用、伪类错乱甚至布局抖动。
正确写法要立刻清空 onload 句柄:
立即学习“前端免费学习笔记(深入)”;
<link rel="preload" href="big.css" as="style" onload="this.onload=null;this.rel='stylesheet'"> <noscript><link rel="stylesheet" href="big.css"></noscript>
注意点:
- 不加
this.onload=null→ Safari 15~17 每次 DOM 更新都可能再执行一次onload - 缺少
<noscript>→ JS 被禁用时页面完全无样式,属于可访问性失败 - 不要用
fetch()+insertRule动态注入:大体积 CSS 解析耗时高,容易阻塞主线程,且无法利用浏览器原生的 CSSOM 并行解析能力
HTTP/2 下仍要拆分,但按“变更频率”而非“功能模块”
低网速用户往往也用老旧设备或弱信号基站,TCP 连接不稳定。HTTP/2 多路复用虽好,但单个大 CSS 文件一旦传输中断就得重传整块;而按变更频率拆分后,高频更新部分(如业务组件)体积小、重传快,低频部分(如 reset.css)可长期缓存。
推荐拆分策略:
-
base.css:含normalize.css、基础重置、字体定义 —— 设置Cache-Control: public, max-age=31536000, immutable -
ui.css:组件库样式(如 antd、element-plus)—— 用内容哈希命名,CDN 缓存 1 年 -
page.css:当前路由专属样式 —— 不设 long cache,配合 ETag 强校验
避免踩坑:
- 别把
page.css和ui.css合并成一个文件:前者每周发版都变,后者半年才升一次版本,合并后每次更新都让客户端丢弃整个缓存 - Vite 用户务必关掉
build.cssCodeSplit: true(默认是true),否则它会把每个@import或css模块都拆成独立 chunk,反而增加请求数
CDN 缓存头没配对,再快的 CDN 也白搭
很多团队上了 Cloudflare 或阿里云 CDN,但 curl -I https://yoursite.com/main.css 返回的仍是 Cache-Control: no-cache —— 这意味着所有请求都回源,CDN 彻底失效。
必须检查并强制覆盖的响应头:
- 静态资源服务器(Nginx)里加:
add_header Cache-Control "public, max-age=31536000, immutable"; - 若用 Vercel,
vercel.json中需显式配置headers规则,不能只靠static.json或文件后缀匹配 - 确认 CDN 控制台没开启“忽略查询参数”且没勾选“强制缓存过期时间”这类反向覆盖选项
最后提醒:低网速优化的核心不是“让 CSS 下得更快”,而是“不让它拖慢 HTML 解析”。内联关键路径、切断非关键依赖、确保缓存真正生效,这三件事做扎实了,比压缩 20% 体积更管用。


















