link标签必须放在<head>中且在所有<script>之前,否则JS可能读取未就绪样式,引发layout thrashing、错误计算或逻辑失效;CSS虽不阻塞HTML解析,但会阻塞渲染树构建和JS执行。

link 标签必须放在 <head> 中,且必须在任何 <script>(含内联脚本)之前——否则 JS 执行时可能读到未就绪的样式,触发 layout thrashing 或报错。
为什么 CSS 必须在 JS 前加载并解析完成
CSS 不阻塞 HTML 解析,但会阻塞渲染树(render tree)构建和 JS 执行。浏览器遇到无 async/defer 的 <script> 时,会暂停 JS 执行,直到它前面所有 CSSOM 构建完毕。若 link 在 script 后,JS 就可能读取到错误的 getComputedStyle 值、误判元素尺寸,或因依赖样式类(如 .is-loaded)而逻辑失效。
- 内联脚本也受此约束:
<script>console.log(getComputedStyle(document.body).backgroundColor)</script>若前面没link,返回空字符串或默认值 - 即使 CSS 文件体积小,网络延迟或服务端慢响应也会放大问题
- IE 和旧版 Edge 下,该阻塞更严格;Chrome/Firefox 虽有预加载扫描,但仍按顺序等待 CSSOM 就绪
常见错误写法与修复方式
以下写法都会导致 JS 执行早于 CSS 生效:
-
<head><script src="init.js"></script><link rel="stylesheet" href="app.css"></head>→ 把link移到script前 -
<head><link rel="stylesheet" href="theme.css"><style>body{opacity:0}</style><script>document.body.style.opacity=1</script></head>→ 内联<style>不阻塞 JS,但外部 CSS 未就绪时,opacity:0可能还没应用,JS 已执行 - 用
@import替代link:它在 CSS 内部加载,等效于把规则“粘贴”到最后一个 CSS 文件末尾,实际加载时序不可控,且强制串行
多个 CSS 文件的引入顺序怎么排
顺序即层叠优先级,也是实际应用顺序。后加载的 CSS 规则会覆盖同权重的前一个文件中的规则,且 !important 无法跨文件生效。
立即学习“前端免费学习笔记(深入)”;
- 基础重置(
reset.css或normalize.css)放最前 - 通用工具类、布局框架(
grid.css、utils.css)紧随其后 - 页面/组件专属样式(
home.css、modal.css)放最后,确保可覆盖前面所有规则 - 主题变量(
:root { --primary: #007bff; })必须出现在首个 CSS 文件中,否则后续文件读不到变量
动态插入 CSS 时容易忽略的关键点
用 document.createElement('link') 插入 CSS 时,浏览器不会自动等待它加载完成再执行后续 JS。
- 不加控制直接写:
const link = document.createElement('link'); link.href = 'dark.css'; document.head.appendChild(link); initTheme();→initTheme()极大概率执行时样式未生效 - 正确做法是监听
link.onload或封装为 Promise:link.onload = () => initTheme(); - 多个动态 CSS 插入需手动串行,不能依赖插入顺序——它们的
onload触发时机不确定 - 微前端场景下,子应用注入的 CSS 若未协调加载时序,极易覆盖主应用样式或被覆盖
真正决定 CSS 是否“已生效”的,不是 DOM 中 link 元素是否存在,而是 CSSOM 是否构建完成、是否已参与 render tree 计算。这个过程对开发者不可见,只能靠位置约束和加载回调来保障。


















