<link> 放在 <head> 更安全,因其能阻塞 HTML 解析以确保样式就绪、避免 FOUC,且支持预加载、媒体查询和 rel="preload" 等机制;放 <body> 仅适用于 JS 动态插入的非关键 CSS。

为什么 <link> 放在 <head> 里更安全
浏览器解析 HTML 是从上到下流式进行的,遇到 <link rel="stylesheet"> 时会发起 CSS 请求,并**阻塞后续 HTML 解析与 DOM 构建**(直到该 CSS 加载并解析完成)。放在 <head> 中能确保样式在页面内容渲染前就绪,避免 FOUC(Flash of Unstyled Content)——也就是页面先弹出无样式的白板,再突然“跳”成带样式的状态。
常见错误现象:<link> 放在 <body> 底部,但页面已有大量 <div>、<p> 等元素被解析并尝试渲染,此时浏览器发现没有可用样式规则,只能用默认样式画一遍;等 CSS 终于加载完,再重绘一次。用户肉眼可见“闪一下”。
- 现代浏览器对
<head>中的<link>有预加载优化(如 preload scanner),能提前发起请求 -
<link>在<head>中可被<noscript>或媒体查询(media="print")正确识别和延迟加载 - 若用
rel="preload"配合as="style",也必须放在<head>,否则无效
<link> 放在 <body> 的少数合理场景
不是绝对禁止,而是要明确知道你在做什么、承担什么代价。唯一被广泛接受的例外是「非关键 CSS 的异步加载」——比如打印样式、暗色主题切换器的备用样式、或仅用于某个第三方组件的隔离样式。
这时通常配合 JavaScript 控制加载时机,例如:
立即学习“前端免费学习笔记(深入)”;
<script>
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = '/print.css';
link.media = 'print'; // 关键:初始设为不生效的 media
document.head.appendChild(link);
</script>- 不能直接写
<link href="...">在<body>里,否则仍会阻塞解析 - 必须用 JS 动态插入 + 初始
media设为无效值(如media="not all"),等需要时再改回media="print"或media="all" - 注意:动态插入的 CSS 不会触发 CSSOM 重建,但会触发重排重绘,需评估性能影响
内联 <style> 标签的位置影响比 <link> 更直接
<style> 是同步解析执行的,无论放哪都会立即阻塞 HTML 解析。但它没有网络请求开销,所以位置选择核心在于「作用域可见性」和「维护成本」。
- 放在
<head>:样式对整个文档有效,符合语义直觉;但若只用于页脚某个组件,会导致全页面都多加载一段无用 CSS - 放在
<body>某个元素上方(如紧贴<footer>前):CSS 规则只对后续节点生效(因为 CSS 选择器匹配基于 DOM 树,而非声明顺序),但容易误判“它到底管不管用” - 若使用
@import(不推荐),必须放在<style>开头,且会引发额外阻塞,比<link>更慢
真实陷阱:<style> 放在 <body> 中间,结果发现 .header { color: red; } 并没让顶部 <header> 变红——因为那段 <style> 出现在 <header> 元素之后,而 CSS 不会向上匹配已解析的节点。
现代实践:关键 CSS 内联 + 剩余 CSS 异步加载
真正兼顾性能与体验的做法,不是纠结“放 head 还是 body”,而是拆分:把首屏必需的 CSS 提取出来,用 <style> 内联在 <head>;其余 CSS 用 <link rel="preload"> 提前拉取,再通过 onload 注入或 JS 动态挂载。
示例结构:
<head> <style>/* 首屏关键样式 */</style> <link rel="preload" href="main.css" as="style" onload="this.onload=null;this.rel='stylesheet'"> <noscript><link rel="stylesheet" href="main.css"></noscript> </head>
-
onload回调中把rel="preload"改成rel="stylesheet",才能真正应用样式 -
this.onload=null是防重复执行,尤其在某些旧版 Safari 中可能触发多次 -
<noscript>是兜底,确保禁用 JS 时仍能加载主样式
这个模式下,<link> 的物理位置仍在 <head>,但逻辑上实现了“非阻塞加载”。硬塞进 <body> 只会让问题更难调试。











