移动端首屏白屏超300ms主因是css阻塞渲染,解法是让html解析时即应用首屏必需样式;需内联≤14kb critical css、异步加载非关键css、合理使用content-visibility与contain-intrinsic-size,并规避@import及media="all"等陷阱。

移动端首屏白屏时间超过 300ms,基本可以判定是 CSS 加载或解析阻塞了渲染。核心解法就一条:让浏览器在解析 HTML 的同时,就能拿到并应用首屏必需的样式。
为什么在移动端特别危险
移动端网络波动大、带宽低,<link rel="stylesheet"> 默认会阻塞 HTML 解析和首次绘制。哪怕只有 20KB 的 CSS 文件,也可能因 DNS、TCP、TLS 延迟,导致首屏延迟 800ms+。更糟的是,@import 会串行加载、无法被预加载器识别,media="all"(默认值)等于明示“立刻用我”。
- 不要把所有样式打包进一个
main.css再全量加载 - 避免在
中写@import url("xxx.css") - 别依赖 “CSS 很小所以没事”——HTTP/2 下仍受队头阻塞影响
内联 Critical CSS 的实操要点
只内联首屏可见区域(viewport 高度内)真正用到的规则,体积控制在 ≤14KB。工具链必须自动化,人工提取不可靠且易过期。
- Vite 项目用
vite-plugin-critical,它支持按mobile: 375px和tablet: 768px分别提取,dev 阶段就能验证 - Webpack 项目推荐
critters(纯 AST 分析,不依赖 Puppeteer,SSR 友好) - 内联后的
<style data-critical></style>块不能含@import、@font-face或带 URL 的background-image,否则会触发额外请求 - Next.js 等 SSR 框架,应在
getServerSideProps或generateStaticParams阶段动态注入,而非硬编码
非关键 CSS 必须异步加载
媒体查询不是万能的——media="screen" 仍会阻塞,只有当前环境**不匹配**的 media 值才降为低优先级。真正要“不阻塞”,得主动干预加载时机。
- 对模态框、折叠菜单等交互前需就绪的样式,用
<link rel="preload" as="style" onload="this.rel='stylesheet'"> - 对打印、深色模式等条件性样式,用精确匹配表达式:
media="(prefers-color-scheme: dark)"或media="screen and (max-width: 768px) and (orientation: portrait)" - 完全不用的样式(如未启用的主题、废弃组件)应从构建产物中彻底移除,用
PurgeCSS或UnoCSS按需生成 -
media="print"+ JS 切换是 hack,仅作 fallback;现代项目优先走rel="preload"路线
content-visibility 对长列表的生效条件
加了 content-visibility: auto 却没效果?大概率是漏了 contain-intrinsic-size,或者加错了层级。
- 只加在外层容器(如
.list),不要给每个<li>单独加——滚动条会因尺寸重算而跳动 -
contain-intrinsic-size必须设为合理占位高度,比如卡片固定高 200px 就写200px,不能写0或留空 - Firefox 和 Safari(截至 2026 年中)仍不支持该属性,需用虚拟滚动或 IntersectionObserver 手动控制渲染范围
- 搭配
contain: layout style paint可进一步限制样式泄漏和重绘范围,但需确认父容器无意外布局依赖
最关键的细节往往藏在边界条件里:Critical CSS 提取 viewport 宽度是否匹配真实设备、contain-intrinsic-size 是否与实际渲染高度一致、rel="preload" 的 as="style" 是否被 CDN 或代理误改 MIME 类型——这些地方一错,优化就归零。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











