render blocking 实测:26 个首页 285 张样式表,没有一张写了 media=print

render blocking 是样式表的默认行为,而 26 个被优化得最狠的首页没有一个绕开它:285 张外链样式表要么不写 media 要么写 all,几乎每份性能指南都推荐的 media=print 写法出现 0 次。

改写与发布5 分钟读完1325 次阅读
render blocking 实测:26 个首页 285 张样式表,没有一张写了 media=print

实测 · 2026-08-27 · 26 个首页 · 285 张样式表 · 一次抓取 · 只看原始 HTML

样本 / 口径:2026-08-15 起一直在用的那 30 个站,2026-08-27 各抓一次,浏览器 UA,不渲染。逐条读出 rel 含 stylesheet<link>,加上所有 rel="preload" as="style" 与内联 <style> 块,记下 media 属性、相对 </head> 的位置、以及内联块的字节数。

render blocking 是样式表的默认行为,而在 26 个被优化得最狠的首页上,没有一个站选择绕开它。285 张外链样式表要么不写 media,要么写 media="all",两者等价。media="print" ——几乎每份性能指南都会推荐的那个延迟加载写法——出现 0 次。倒是有五个站干脆不用外链样式表,把 CSS 全部内联进文档。

怎么测的

每站一次 HTTP 请求,不渲染,不重试。纳入规则跑数前写死:最终状态 200、解压后正文至少 10,000 字节、正文里有 <body。30 个里挂了 4 个 —— www.canva.com 返回 429,stackoverflow.commedium.com 返回 403,www.reddit.com 返回一个 8,393 字节的空壳,剩下 26 个。

在 head 还是在 body,是按解析器自己的状态判的,不是按字节偏移猜的:只有解析器还没遇到 </head> 时读到的那些才算在 head 里。这个区分撑起了整篇,因为 MDN 的 link 参考页 2026-08-27 读到的原文是:「Only link elements in the document's <head> can possibly block rendering. By default, a link element with rel="stylesheet" in the <head> blocks rendering when the browser discovers it during parsing.」

# 数一下自己页面上真正阻塞的那几张
curl -sL --compressed -A 'Mozilla/5.0' https://example.com/ \
  | sed -n '1,/<\/head>/p' \
  | grep -o '<link[^>]*stylesheet[^>]*>' \
  | grep -cv 'media="print"'

这次没测任何一项耗时。没有 timing,没有瀑布图,不渲染。一张走热缓存 CDN 的阻塞样式表几乎不花钱,而这次抓取分不出它和昂贵的那种。

render blocking 的那 228 张:全在 head,没有一个 media 属性给它们放行

285 张外链样式表里,228 张在 head。而这 285 张写的要么什么都没有,要么是 all

数的是什么总数站数
外链样式表28526 里 21
— 在 <head>22821
— 在 <body>574
没写 media226
media="all"59
media="print"00
blocking="render"00
preload as=style405

分布很散。中位数是每站四张;纽约时报 69 张,Linear 54 张,GitHub 41 张。另一头,Figma 和 Hacker News 各一张。阻塞这件事上最极端的是 Linear:54 张全在 head,另外还内联了 250,218 个字符的 CSS。

人人推荐的那个写法,26 个站里 0 个在用

对首屏用不到的样式表,标准做法是写 media="print" 加一个 onload,等文件到了再把 media 改回 all。浏览器照样下载,但用低优先级,而且不会为它等。

我们第一遍查出 8 张带 onload 的样式表,全在 slack.com,本篇初稿因此写的是「26 个站里有 1 个延迟加载 CSS」。然后我们回落盘的字节里看了一眼。Slack 那个 handler 是 window._cdn ? _cdn.ok(this, arguments) : null,旁边还有一个 onerror 双胞胎——CDN 的成功回调,从头到尾没碰 media。那 8 张和其他张一样阻塞,真实数字是 0。

这批站要么等 CSS,要么把 CSS 塞进文档。没有一个站走中间那条路。

走第二条路的有五个。它们一张外链样式表都不发,规则全写在 <style> 块里——没有请求,自然谈不上为请求等待。

站点内联字符块数
www.framer.com511,7707
www.wired.com398,5435
www.shopify.com245,1332
www.bbc.com88,3503
www.wikipedia.org60,3823

这是一笔交易,不是白赚:那些字节每一次页面加载都跟着 HTML 走一遍,而且没法单独缓存。全样本 155 个内联块共 2,075,948 个字符,每站中位数 13,517。反方向也有五个站一个字符都不内联:nextjs.orgstripe.comslack.comsupabase.comnews.ycombinator.com

57 张样式表在 body 里,43 张属于同一个视频播放器

样式表写在 body 里是合法的。MDN 原文:「the stylesheet link type is body-ok, and therefore <link rel="stylesheet"> is permitted in the body.」按同一份文档,它也不在能阻塞渲染的那一类里——只有 head 里的 link 能。

四个站把样式表放在了那儿。纽约时报有 43 张,而且不是散落的:我们从落盘字节里抽查的每一张都属于同一个视频播放器包 betamax,文件名形如 player-0.3.28-gTxBzUvg.css。GitHub 11 张,Webflow 2 张,Railway 1 张。纽约时报的 body 从文档第 202,375 个字符才开始,所以那 43 个请求按定义就是被晚发现的。

那个位置是性能决策还是组件恰好渲染在那里,我们说不清。两种情况产生的标记一模一样,而我们没问过任何人。

这对你意味着什么

值得动手的数字不是 285,是你自己 head 里那几张——每一张都是首屏出现之前的一次网络往返。

  • 先数一遍。上面那条命令,结果通常比你担心的少,或者比你以为的多得多
  • 确实首屏用不到的那张,写 media="print" 加 onload 改回 all。这批站 0 个这么做,说明它要么比看起来难,要么比听起来没用
  • 别因为五个大站这么干就把整份样式表内联。Framer 那 511,770 个字符每次加载都跟着走,永远进不了浏览器缓存
  • 别以为 rel="preload" as="style" 能解掉阻塞。它改的是抓取优先级,样式表那条 link 该等还是等

这件事到底值不值得你花一个下午,取决于搜索引擎公开说过什么,我们把它整理成了 core web vitals 值不值得做。同一个 head 里脚本那一半的统计在 async defer 实测 27 个首页,连接层的提示在 27 个首页 650 条资源提示。想看引擎在任何样式生效之前从你的页面拿到了什么,可以 看看 QueryWin 怎么运转

常见问题

你们是怎么测的?

2026-08-27 每个首页发一次请求,浏览器 UA,正文落盘,link 与 style 元素用 HTML 解析器读一遍并记录 head/body 位置,所有发出来的数字都是从落盘文件重算的。

什么样的样式表才算 render blocking?

在 head 里、解析时被发现、media 与当前设备匹配的 <link rel="stylesheet">。MDN 把它写成默认行为,并补了一句:脚本后来插进去的 link 得显式写 blocking="render" 才会阻塞。

media="print" 真能解掉阻塞吗?

它解掉的是等待,不是下载。浏览器会用较低优先级把文件取回来,中间不为它停,onload 再把 media 值改过来让规则生效。本样本没有一个站在用,所以我们有 26 个数据点和 0 个可参照的实例。

内联 CSS 比外链好吗?

首次访问的首屏,好,因为少一次请求。之后的每一次访问,不好,因为那些字节没法单独缓存,每次都跟着 HTML 再来一遍。这批站里五个选了前一种取舍,另外五个选了完全相反的。

body 里的样式表会拖慢什么吗?

按 MDN 给的定义不会,因为只有 head 里的 link 能阻塞渲染。它们仍然是 57 个在文档很靠后才被发现的请求,而本样本里 43 个属于同一个视频播放器。

render blocking 实测:26 个首页 285 张样式表,没有一张写了 media=print