聊聊css阻塞渲染

好久没更了,发生了比较多的事。首先是换了份工作,觉得之前的工作没什么意思,就主动辞职了,顺便休息一段时间。结果在gap的这段时间内,ai coding发展的特别迅速,前端开发变了天。前端岗位少了很多,agent开发岗位多了很多。但是我说白了,很多公司招agent开发的面试官,他自己懂个屁的agent。然后传统前端岗也是,有些面试官我真的怀疑他的水平,他说我说的回答不对,我确信我是对的,但是没招。

这就是ai冲击下的前端开发现状。没什么人研究技术了,以后懂技术的人越来越少,但是面试的时候会有一群技术不怎么样的人来问你技术,哈哈

怎么说呢,ai确实厉害,你说它对于一些原理啊,提升水平什么之类的,帮助有限,但是实际开发提效太降维打击了。而实际上对原理的理解,对真实水平的提升,除了自己,无人在意。只要你需求完成得快,那ai就是对的。所以确实是有点没心气了,心情好就写一写记录一下,心情不好的话还是好好活着更重要。

css和页面渲染

说回正题,好久没写了,第一篇还是写的简单一点,就聊聊八股里面经常提到的,css会阻塞页面渲染。

八股里面经常说,浏览器要渲染页面,把html标签解析成dom树,把css解析成cssom树,然后把两棵树合并成render树。而解析css会阻塞渲染,因为不知道样式就无法渲染。

通常情况下,放在 head 中的css会阻塞首次绘制,浏览器会等待它加载并解析完成。

如果css在body里面?

通常来讲,css都是放到head里的,便于浏览器更早的发现css并下载。

但是如果css放到了body的里面,那么浏览器可能会先把当前已经解析结束的内容,先做一次渲染。然后等css下载并解析完成后,再重新渲染一次。

我们可以自己写个demo来试验一下,用express起个简单的node server,写个html文件,css文件,然后让node server收到请求的时候返回相应的html或css。只不过返回css的时候,加上几秒钟的延迟,便于我们观察效果。

相关代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<!-- public/index.html -->

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>观察 3 秒后的样式加载</title>
</head>
<body>
<main>
<h1>等待 3 秒的 CSS</h1>
<p>样式表引用位于第一张和第二张卡片之间,服务器延迟 3 秒返回 CSS。</p>

<section class="cards" aria-label="样式观察区">
<article class="card">
<h2>颜色</h2>
<p>CSS 生效后,这张卡片会变成浅紫色,页面背景变为深色。</p>
</article>

<link rel="stylesheet" href="/styles.css">

<article class="card">
<h2>布局</h2>
<p>宽屏上,三张卡片会横向排列;窄屏上则纵向排列。</p>
</article>
<article class="card">
<h2>细节</h2>
<p>观察字体大小、留白、圆角和阴影在样式生效后的变化。</p>
</article>
</section>
</main>

</body>
</html>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/* public/style.css */

:root {
color-scheme: dark;
font-family: system-ui, -apple-system, "Segoe UI", "Microsoft YaHei", sans-serif;
background: #11151e;
color: #eef1f8;
line-height: 1.7;
}

* { box-sizing: border-box; }
body { margin: 0; }
main { max-width: 1080px; margin: 0 auto; padding: 64px 24px; }
h1 { font-size: clamp(36px, 6vw, 64px); line-height: 1.18; letter-spacing: -0.04em; margin: 20px 0; }
h2 { margin: 10px 0; font-size: 23px; }
.cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 20px; margin: 36px 0; }
.card { padding: 28px; border-radius: 20px; color: #242138; background: #d9ccff; box-shadow: 0 18px 35px #0003; }
.card:nth-of-type(2) { background: #bcebdc; }
.card:nth-of-type(3) { background: #ffd6ae; }
.card p { margin-bottom: 0; }

@media (max-width: 720px) {
main { padding: 36px 18px; }
.cards { grid-template-columns: 1fr; }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//server.js

const express = require('express');
const path = require('node:path');
const { setTimeout: delay } = require('node:timers/promises');

const app = express();
const port = Number(process.env.PORT || 3000);

app.use(async (request, response, next) => {
response.set('Cache-Control', 'no-store');
if (request.path.endsWith('.css')) {
await delay(3000);
}
next();
});

app.use(express.static(path.join(__dirname, 'public'), {
cacheControl: false,
etag: false,
lastModified: false,
}));

app.listen(port, '127.0.0.1', () => {
console.log(`Open http://localhost:${port}`);
});

当我们执行node server.js命令启动服务器,然后访问localhost:3000的时候,我们最先看到的是这样

而过了几秒后,页面变成了这样

以上是在chrome155中试验的结果。浏览器遇到 body 内的样式表后,会暂停后续 HTML 的解析,但允许前面已经构建的 DOM 被绘制。因此,CSS 加载前只有第一张卡片出现;加载完成后,解析继续,后两张卡片才出现,同时样式应用到整个页面。

这说明浏览器如果遇到了body里面的css,并不会傻等这个css下载并解析完成,所以人们常背的八股里css阻塞渲染,并不是绝对的,没有一直不渲染等你的义务,可以先把当前准备好的内容先渲染出来,这也是浏览器的增量解析的机制。这就是FOUC(无样式内容闪烁)现象,体验很差,所以我们应该避免把css写到body里面。

死背八股,死路一条。很多人痛恨八股,觉得死记硬背没有意义。但实际上八股里面的内容恰恰都很有用,只不过需要结合实际去理解。背而不懂,没有意义。


聊聊css阻塞渲染
https://miku03090831.github.io/2026/10/11/聊聊css阻塞渲染/
作者
qh_meng
发布于
2026年10月11日
许可协议