浏览器资源加载时序与阻塞行为解析
1.前言
本文通过构造包含本地与远程脚本、图片等资源的 HTML 页面,结合人为延迟加载(1000ms),实测浏览器在解析文档过程中的资源加载顺序与脚本执行阻塞行为。重点验证以下问题
- 同步 script 标签是否阻塞 HTML 解析?
- head 与 body 中的脚本执行顺序如何?
- DOMContentLoaded 与 window.onload 的触发时机与资源加载的关系。
2.从输入 URL 到页面呈现:发生了什么?
当用户在浏览器地址栏输入 URL 并回车后,浏览器会依次执行以下步骤:
-
DNS 解析
将域名(如 example.com)解析为对应的 IP 地址。解析顺序通常为:浏览器缓存 → 操作系统缓存 → 路由器缓存 → 本地 DNS 服务器 → 根域名服务器(递归查询)
-
建立 TCP 连接(三次握手):
- 客户端向服务器发送 SYN 报文(SYN=1),请求建立连接;
- 服务器收到后回复 SYN-ACK 报文(SYN=1, ACK=1),表示同意连接;
- 客户端再发送 ACK 报文(ACK=1)确认,至此 TCP 连接建立成功。
-
发送 HTTP 请求
浏览器通过已建立的 TCP 连接,按照 HTTP/HTTPS 协议发送请求(如 GET /index.html) -
服务器处理并返回响应
服务器接收请求,处理逻辑(如读取文件、调用数据库等),生成 HTTP 响应(含状态码、响应头、HTML 内容等)并返回 -
浏览器解析与渲染
- 接收 HTML 后,浏览器开始解析 HTML 构建 DOM 树;
- 遇到 CSS 则构建 CSSOM 树;
- 合并 DOM 与 CSSOM 生成 Render Tree;
- 执行布局(Layout)与绘制(Paint),最终将页面呈现给用户
-
关闭 TCP 连接(四次挥手)
3.实验设计思路(script 的加载与执行机制)
为了探究浏览器解析 HTML 和加载脚本的顺序,我在 head 和 body 中分别插入:
- 本地内联脚本;
- 远程脚本(通过本地服务器提供,每个脚本人为延迟 1000ms 模拟网络耗时);
- 多张远程图片(同样延迟加载)。
通过 console.log 输出时间戳,并监听 DOMContentLoaded 与 window.onload 事件,观察执行时序
关键代码片段(简化展示)
<head>
<script>console.log('head script 1', Date.now())</script>
<script src="http://127.0.0.1:80/script0.js"></script> <!-- 延迟 1s -->
<script>console.log('head script 2', Date.now())</script>
</head>
<body>
<img src="http://127.0.0.1:80/image1.jpg"> <!-- 延迟 1s -->
...
</body>
<script>console.log('body script before', Date.now())</script>
<script src="http://127.0.0.1:80/script1.js"></script> <!-- 延迟 1s -->
<script>console.log('body script after', Date.now())</script>
同时监听两个关键事件:
document.addEventListener('DOMContentLoaded', () => {
console.log('DOMContentLoaded', Date.now());
});
window.onload = () => {
console.log('window.onload', Date.now());
};
4.实验观察与结论
资源加载是分组并行的
浏览器会将同域资源按并发限制(通常 6 个/域名)分批请求。如图所示,资源被分成多组,每组并行加载,前一组完成后再发起下一组请求(实际行为受 HTTP/2 多路复用影响,但本实验基于 HTTP/1.1)。

script 是阻塞式加载与执行
- 浏览器按 HTML 顺序解析文档;
- 遇到 script(无论位于 head 还是 body)时:
- 若为外部脚本(src 属性),则暂停 HTML 解析,下载并执行该脚本;
- 若为内联脚本,则立即执行;
- 所有脚本必须按顺序执行:前一个脚本未加载/执行完,后续脚本(包括 HTML 解析)会被阻塞。
因此,执行顺序为:
head 内联 → head 远程脚本(逐个等待)→ head 后续内联 → body HTML 渲染 → body 内联 → body 远程脚本(逐个等待)→ ...
DOMContentLoaded 与 window.onload 的触发时机
- DOMContentLoaded:
当初始 HTML 文档完全加载和解析完成,且所有同步脚本(非 async/defer)执行完毕时触发。
此时不等待图片、样式表、iframe 等外部资源加载完成。 - window.onload:
当页面所有资源(包括图片、视频、样式、脚本等)全部加载完毕后触发
DOMContentLoaded 在所有同步脚本执行后触发,而 window.onload 在所有远程资源(如图片)加载完成后才触发。



浙公网安备 33010602011771号