CSS必须放在head里,因为浏览器自上而下解析HTML时,link样式表会阻塞渲染树构建但不阻塞DOM解析,放body中易导致白屏或FOUC;JS默认不应放head里,因其会完全阻塞DOM解析,应置于/body前或使用defer/async。

为什么CSS必须放在head里
因为浏览器解析HTML是自上而下,遇到会立即发起请求并阻塞渲染树(Render Tree)构建,但不会阻塞DOM树解析。如果把CSS放到
里,DOM树可能先建完,但渲染树迟迟无法生成,导致页面白屏或闪动——直到CSS加载并解析完毕才开始绘制。放在中能确保样式就绪时DOM元素刚好解析到对应位置,实现“边解析边渲染”。常见错误现象:FOUC(Flash of Unstyled Content),即页面先显示无样式的HTML内容,再突然套上样式;尤其在弱网或CSS体积大时明显。
-
<link rel="stylesheet">必须在内,且尽量靠前 - 避免在
中动态插入<link>标签来加载关键CSS - 内联首屏关键CSS(如骨架屏样式)可放
中的<style></style>里,减少RTT
为什么JS默认不该放在head里
脚本在
中执行时,会完全阻塞DOM树构建:浏览器必须暂停HTML解析、下载并执行JS后才能继续。这意味着用户看到的可能是长时间空白页,尤其当JS体积大或网络慢时。使用场景差异:document.write() 或依赖全局变量初始化的旧式库(如早期jQuery插件)确实需要提前载入,但现代代码基本不需要。
立即学习“前端免费学习笔记(深入)”;
- 绝大多数业务JS应放在
前,紧贴闭合标签 - 若必须在中引入JS,请加
defer属性(<script defer src="app.js"></script>),它让下载与DOM解析并行,执行推迟到DOM树构建完成 -
async仅适用于完全独立、无DOM依赖的脚本(如统计埋点),执行时机不可控,可能早于DOM就绪
head里放JS的例外情况与风险
有些脚本看似“必须放head”,实则是误解。比如用 $(function(){}) 或 DOMContentLoaded 包裹逻辑,本质是为了等DOM就绪——这恰恰说明脚本不该依赖前置执行时机,而是该挪到
真实例外只有两类:同步获取设备/UA信息用于服务端渲染降级(极少见),或内联小段JS用于设置或预加载资源(如fetch()预热API)。
- 放在head里的JS,哪怕加了
defer,仍会抢占主线程带宽,影响首屏资源并发下载 - Webpack/Vite 打包产物若含
runtime或manifest脚本,也建议用defer,而非默认同步加载 - 不要在head里写
document.getElementById()类操作——DOM节点根本还没解析出来
检查是否真有性能收益
别只信“规范”,要验证。打开 Chrome DevTools → Network 面板,勾选 Disable cache,刷新页面,观察 Waterfall 图中关键资源的时间线:
- CSS是否在
HTML解析完成前已Finished?如果不是,考虑启用 HTTP/2 Server Push 或预加载(<link rel="preload" as="style">) - JS是否在
DOMContentLoaded时间点之后才执行?如果是,说明它没阻塞解析,但可能拖慢交互时间(TTI) - 对比移动设备 throttling 下的白屏时长:head里多一个100KB的JS,可能让FCP延迟800ms以上
最易被忽略的一点:即使你把JS放到了前,如果它内部大量同步操作(如未分片的for循环处理万条数据),依然会让主线程卡死——位置只是起点,执行逻辑本身才是瓶颈。



















