必须放里,因其虽不阻塞HTML解析,但会阻塞DOM渲染和后续执行;若置于底部,将导致DOM树构建完毕后仍需等待CSSOM就绪才能合成页面,引发白屏延长、DOMContentLoaded延迟及FCP超2.5s。

外部资源引用(<link rel="stylesheet">、<script src></script>)是性能瓶颈高发区,关键不在“引不引”,而在“怎么引”——错放位置、错用属性、错估依赖,都会让首屏白屏延长 1–3 秒。
为什么 <link rel="stylesheet"> 必须放 里
它不阻塞 HTML 解析,但会阻塞 DOM 渲染和后续 <script></script> 执行。如果丢到 底部,浏览器会先建完完整 DOM 树,再等 CSSOM 就绪才合成页面,导致 DOMContentLoaded 延迟、用户看到长时间白屏。
- 真实错误现象:
Network面板里 CSS 请求排在 HTML 后面,FCP(首次内容绘制)超过 2.5s - 例外可后置:非关键 CSS(如打印样式、暗色主题)可用
media="print"+onload="this.media='all'" - 绝对别用
@import:它在 CSS 文件内部生效,强制串行加载,实测多拖慢FCP300ms+
defer 和 async 的选择逻辑不能靠猜
两者都让 <script src></script> 不阻塞 HTML 解析,但执行时机和顺序完全不同:
-
async:下载完立刻执行,顺序不确定;适合无依赖的独立脚本(如analytics.js) -
defer:等整个 HTML 解析完、DOM 构建完成后再按书写顺序执行;适合操作 DOM 或有依赖链的脚本(如vue.js→app.js) - 内联脚本(没
src)不支持这两个属性,写了也无效 -
type="module"默认等效于defer,且支持静态导入分析,推荐新项目优先用
非关键 CSS 怎么真正“不阻塞”
仅靠 media="print" 不够,得配合 JS 触发切换,否则浏览器可能根本不下载该文件。更稳妥的是用 <link rel="preload"> 提前抓取:
立即学习“前端免费学习笔记(深入)”;
<link rel="preload" href="non-critical.css" as="style" onload="this.rel='stylesheet'"> <noscript><link rel="stylesheet" href="non-critical.css"></noscript>
-
preload让浏览器在早期就发起请求,不阻塞渲染,也不影响优先级 -
onload回调确保样式表加载完成后才激活为stylesheet -
<noscript></noscript>是兜底:禁用 JS 时仍能加载,避免样式丢失
最容易被忽略的点:CSS 和 JS 的加载策略必须协同设计。比如你把 main.js 设了 defer,却把它的依赖样式 main.css 放在 末尾,JS 执行时 DOM 已就绪,但样式还没进 CSSOM——结果就是闪动、布局错乱、getComputedStyle 拿不到预期值。



















