关键CSS文件被阻塞首屏渲染怎么办?
我在优化首屏加载时发现,即使把CSS文件压缩到5KB,页面仍然会卡在“render”阶段。尝试过把CSS内联和使用都没效果,用Lighthouse检查显示这个CSS还是关键路径的一部分,该怎么让它不影响首屏渲染?
代码结构是这样的:<link rel="stylesheet" href="/styles/main.css" rel="external nofollow" rel="external nofollow" >,里面包含了整个项目的样式。我试着把首屏需要的样式抽出来做成critical.css,但合并到HTML后其他页面样式反而乱了。
<!-- 现在的HTML结构 -->
<head>
<style>/* 内联的critical样式 */</style>
<link rel="stylesheet" href="/styles/main.css" rel="external nofollow" rel="external nofollow" >
</head>
用Chrome开发者工具的Coverage标签发现,main.css有80%未使用的代码,但不知道怎么安全地拆分而不影响布局。有没有更好的拆分策略或配置方法?
第一步,确认你现在用的 critical CSS 真的是首屏最小必要样式。可以用 Puppeteer 或 Chrome DevTools 手动录一次首屏快照,提取 body 前几屏元素用到的 CSS 规则。别靠猜,很多人内联的 critical 实际包含了非首屏类名。
第二步,对 main.css 做代码分割。既然 Coverage 显示80%未使用,说明你项目用了组件化但打包时全量引入了。比如你用了 Tailwind 或 Bootstrap 这类框架,得开 purge 功能。Tailwind 要配 content 字段扫描模板文件,Bootstrap 如果是自己引入的 SCSS,就只 import 需要用的模块,别直接引全部。
第三步,把 main.css 改成异步加载。你现在是普通 link 标签,会阻塞。改成这样:
或者更简单的用 loadCSS 方案:
配合 JS 异步注入:
第四步,按路由或页面拆分 CSS。如果你有多个页面,不要共用一个 main.css。用构建工具比如 Webpack 或 Vite,把样式按 page 分离,首页只加载 home.css,用户页加载 user.css。Vite 里用 dynamic import 或 splitChunks 就能实现。
最后,上线前用 Lighthouse 再跑一遍,看“Eliminate render-blocking resources”这条是否消失。如果还有卡顿,检查 network 面板里 main.css 是否和其他关键资源争抢带宽,可以加 media 属性降优先级:
<link rel="stylesheet" href="/styles/main.css" media="print" onload="this.media='all'">这样至少不会抢占首屏渲染。
你遇到的问题是典型的关键CSS阻塞渲染。既然你已经用Lighthouse检查过,那你也知道需要提取首屏关键CSS,但你没拆分好,导致样式乱套。解决方案分两步走:拆分CSS + 延迟加载非关键CSS。
先用工具帮你抽首屏CSS。我常用的是
puppeteer+critical库,本地跑个脚本:这会帮你提取首屏用到的CSS,并且内联到HTML里。main.css剩下的是非关键CSS,可以延迟加载。
然后在HTML里这样处理:
注意
media="print"是为了不让浏览器默认加载这个CSS,然后用onload="this.media='all'"来触发加载。noscript是为了兼容没开JS的情况。这样拆完之后,main.css就不再是渲染阻塞资源了。Lighthouse再跑一遍,应该会好很多。
最后别忘了加个CSS加载器优化体验:
页面加载完后再加载其他CSS,或者根据路由加载对应样式。这样就能真正拆干净了。