会。head 里的标签顺序会真实影响页面加载:它决定浏览器先发现哪些资源、哪些资源阻塞渲染、同步脚本什么时候中断 HTML 解析。影响幅度从几十毫秒到几百毫秒不等,标签类型不同,影响的机制也不同。
为什么 head 里的顺序会影响加载?
结论:浏览器解析 HTML 是从上到下的单线程流式过程,解析器遇到 <link rel="stylesheet"> 会把它标记为阻塞渲染资源,遇到没有 async/defer 的 <script> 会暂停 DOM 解析、先下载并执行脚本,因此排在前面的标签拥有"优先被处理"的特权。
浏览器里还有一个预加载扫描器(preload scanner),它会在主解析器被脚本卡住时继续往后扫 HTML,提前挑出 <img>、<script>、<link> 等资源地址。这意味着单个同步脚本的阻塞没那么致命,但预加载扫描器不执行 JavaScript——凡是被 JS 动态插入的资源,它都发现不了。
CSS 和 script 谁放前面?
结论:CSS 放在所有 <script> 之前,让 CSS 和 HTML 解析并行下载,比反过来能明显减少首屏空白时间。
原因在于浏览器的一条规则:同步脚本执行前,必须等它之前所有阻塞渲染的 CSS 下载并解析完,因为脚本可能调用 getComputedStyle、读取 offsetHeight 等依赖样式的 API。这个现象叫 CSSOM 阻塞脚本执行。
举例说明:假设 CSS 和 JS 各需 100ms 下载。CSS 在前时,两者并行,JS 在约 100ms 后就能执行;JS 在前、CSS 在后时,虽然预加载扫描器可能同时发起两个请求,但只要 CSS 还没就绪,脚本就不能执行,链路被拉长。
preconnect 和 preload 应该放在哪?
结论:<link rel="preconnect">、<link rel="dns-prefetch">、<link rel="preload"> 应放在 <head> 靠前位置,紧跟 charset 和 viewport 之后,越早越好。
preconnect 提前完成 DNS 查询、TCP 握手和 TLS 握手,省掉的是后续请求的建连时间。在移动网络上一次完整的 DNS + TCP + TLS 往返通常需要 200–500ms,这正是 preconnect 想抢出来的时间。如果把它放到一堆 <meta> 和样式表后面,抢跑的意义就被吃掉了。
preload 同理,它只是提高资源发现优先级,不改变资源本身的加载时机——放晚了等于没用。典型用法是给首屏 LCP 图片、Web Font 加 as="font"、crossorigin 属性。
meta charset 为什么必须放第一个?
结论:<meta charset="utf-8"> 应当作为 <head> 的第一个标签,因为 HTML 标准要求编码声明出现在文档前 1024 字节内。
如果编码声明出现太晚,浏览器会先用默认编码解析前面的内容,之后发现编码声明再重新解析整个文档,属于纯浪费。<meta name="viewport"> 紧随其后,它影响移动端布局宽度,位置靠前能让渲染更早拿到正确视口信息。
推荐的 head 标签顺序是什么?
结论:按"越早需要越靠前"的原则排,参考顺序如下——
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="preconnect"> / <link rel="dns-prefetch">
<link rel="preload">(关键字体、LCP 图片)
<title>、<meta name="description">
- 阻塞渲染的
<link rel="stylesheet">
<script defer src="...">(不带 async 的第三方脚本尽量加 defer)
- 其余
<meta>、favicon、结构化数据
哪些标签的顺序其实无所谓?
结论:<title>、<meta name="description">、<link rel="icon"> 彼此之间的先后顺序对加载时间没有影响;async 脚本之间谁先谁后也不影响下载,只影响执行顺序,而且执行顺序本身还不确定。
defer 脚本则是并行下载、按文档顺序在 DOMContentLoaded 之前执行,所以顺序影响的是执行次序,不是加载速度。真正影响加载速度的只有三类:阻塞渲染的 CSS、阻塞解析的同步脚本、以及各种资源提示(preconnect / preload / fetchpriority)。
怎么验证顺序有没有拖慢加载?
结论:用 Chrome DevTools 的 Performance 面板录制一次首屏加载,看 Network 泳道里各资源的开始时间是否被排在了后面,是最直接的验证方法。
具体步骤:打开 DevTools → Performance → 勾选 Screenshots → 点击录制 → 刷新页面 → 停止录制 → 查看 Frames 和 Network 时间线,定位 FCP(首次内容绘制)之前有没有长时间空档。Lighthouse 的 "Eliminate render-blocking resources" 审计项会直接列出哪些 CSS/JS 拖慢了首屏。也可以用 PerformanceObserver 监听 paint 类型,在代码里上报 FCP 数值做长期对比。
总结一句:head 里标签顺序不影响"能不能加载",但影响"多快能看见内容"。把编码声明放最前、把资源提示紧跟其后、把 CSS 排在脚本之前、给脚本加 defer 或 async,这四步做完,顺序带来的损耗基本就压到最低了。