CHARLIE SAYS

查理如是说
DATE 2026-08-24
THEME
SERIES / NETWORK / P-158 · 网络协议

网络协议 009:知识点串联:输入URL 到页面加载过程详解

本文主要两个目的:第一,将前面涉及网络协议特别是 TCP 协议和 HTTP 协议、DNS 解析等等知识点贯穿;第二,在此基础上介绍浏览器渲染过程,以及为 Web 优化提供基础。

flowchart TB
    A[地址栏输入 URL] --> B[DNS 域名解析 IP<br>缓存 → LDNS → 迭代查询]
    B --> C[建立 TCP 连接<br>三次握手]
    C --> D[发送 HTTP 请求]
    D --> E[服务器处理请求]
    E --> F[返回 HTTP 响应结果]
    F --> G[关闭 TCP 连接<br>四次挥手]
    G --> H[浏览器加载 解析和渲染]

地址栏输入 URL

URL:Uniform / Universal Resource Locator,即统一资源定位符,它实际上就是网站网址,浏览器就是靠 URL 来查找资源位置。可以把 URL 分割成几个部分:协议、网络地址、资源路径。

  • 传送协议:URL 包含协议部分,是浏览器和 www 万维网之间的沟通方式,它会告诉浏览器正确在网路上找到资源位置。最常见的网络传输协议的是 HTTP 协议(超文本传输协议)(https 则是进行加密的网络传输);其他也还有 ftp、file、https、mailto、git 等,还有自定义的协议(私有协议),例如 tencent。不同协议有不同的通讯内容格式。
  • 网络地址:指示该连接网络上哪一台计算机,可以是域名或者 IP 地址,可以包括端口号。
  • 资源路径:指示从服务器上获取哪一项资源。

例如 http://www.quaro.com/question/123456/

  • 协议部分:http
  • 网络地址:www.quaro.com
  • 资源路径:/question/123456/

DNS 域名解析 IP

这里主要贯穿下思路,DNS 涉及相关其它知识点可以参考:DNS 相关详解

基础概念

  • IP 地址:IP 协议为互联网上的每一个网络和每一台主机分配的一个逻辑地址。IP 地址如同门牌号码,通过 IP 地址才能确定一台主机位置。服务器本质也是一台主机,想要访问某个服务器,必须先知道它的 IP 地址。
  • 域名 DN(domain name):域名是为了识别主机名称和组织机构名称的一种具有分层的名称。IP 地址由四个数字组成,中间用点号连接,在使用过程中难记忆且易输错,所以用我们熟悉的字母和数字组合来代替纯数字的 IP 地址,比如我们只会记住 www.baidu.com(百度域名)而不是 220.181.112.244(百度的其中一个 IP 地址)。
  • 计算机域名系统 DNS(Domain Name System or Domain Name Service):它是由域名解析器和域名服务器组成的。域名服务器是指保存有该网络中所有主机的域名和对应 IP 地址,并具有将域名转换为 IP 地址功能的服务器。每个域名都对应一个或多个提供相同服务的服务器的 IP 地址,只有知道服务器 IP 地址才能建立连接,所以需要通过 DNS 把域名解析成一个 IP 地址。

查找域名对应的 IP 地址

通过域名查找 IP 过程:浏览器缓存 -> 系统缓存 -> 本地 DNS 服务器缓存:

  1. 浏览器搜索自己的 DNS 缓存(维护一张域名与 IP 地址对应表);
  2. 搜索操作系统中的 DNS 缓存(维护一张域名与 IP 地址对应表);
  3. 搜索操作系统的 hosts 文件(windows 环境下,维护一张域名与 IP 地址对应表);
  4. 操作系统将域名发送到本地区域服务器(LDNS),进行查找,成功则返回结果(递归查询),失败则发起一个迭代 DNS 请求(迭代查询);
  5. 本地域名服务器 LDNS 将得到的 IP 地址返回给操作系统,同时也将 IP 地址缓存起来;
  6. 操作系统将 IP 地址返回给浏览器,同时将 IP 地址缓存起来。

DNS 迭代查询和递归查询

这里独立一节只是补充强调说明这两个重要的概念,实际上是结合在一起的:

  • 递归查询:客户端与服务器之间属于递归查询,即当客户机向 DNS 服务器发出请求后,若 DNS 服务器本身不能解析,会向另一个 DNS 服务器发出查询请求,最后将结果转交给客户端的过程。服务器必须回答目标 IP 与域名的映射关系。
  • 迭代查询:DNS 服务器之间属于迭代查询。服务器接收到一次迭代查询回复一次结果,这个结果不一定是目标 IP 与域名的映射关系,也可以是其他 DNS 服务器的地址。

请求和响应数据

包括:TCP 连接建立、发送 HTTP 请求、服务端处理、返回 HTTP 结果、TCP 连接关闭。

建立 TCP 连接(3 次握手)

上一步找到 IP 之后,便可以开始建立 TCP 连接了,这里就是我们所说的 TCP 3 次握手。详情可以参考:TCP 协议详解

发送 HTTP 请求

与服务器建立了连接后,就可以向服务器发起请求了。详情可以参考:HTTP 协议详解。请求报文由请求行、请求首部字段、空行以及报文主体组成。

服务器处理请求

服务器端收到请求后的由 web 服务器(准确说应该是 http 服务器)处理请求,诸如 Apache、Ngnix、IIS 等。web 服务器解析用户请求,知道了需要调度哪些资源文件,再通过相应的这些资源文件处理用户请求和参数,并调用数据库信息,最后将结果通过 web 服务器返回给浏览器客户端。

返回 HTTP 响应结果

服务器处理完请求后,就会发送响应结果。响应报文由状态行、响应首部字段、空行以及报文主体组成。响应结果中会有对应的 HTTP 状态码,可分为 5 类(1XX 信息、2XX 成功、3XX 重定向、4XX 客户端错误、5XX 服务器错误)。详情可以参考:HTTP 协议详解

关闭 TCP 连接(4 次挥手)

为了避免服务器与客户端双方的资源占用和损耗,当双方没有请求或响应传递时,任意一方都可以发起关闭请求。与创建 TCP 连接的 3 次握手类似,关闭 TCP 连接,需要 4 次握手。详情可以参考:TCP 协议详解

浏览器加载,解析和渲染

浏览器构成

浏览器内核也称渲染引擎,主要有 3 种:

内核代表浏览器
Trident 内核IE
Webkit 内核Chrome、Safari
Gecko 内核FireFox

浏览器加载

加载过程如下:当浏览器获得一个 html 文件时,会”自上而下”加载,并在加载过程中进行解析渲染:

  • 加载过程中遇到外部 css 文件,浏览器另外发出一个请求,来获取 css 文件;
  • 遇到图片资源,浏览器也会另外发出一个请求,来获取图片资源。这是异步请求,并不会影响 html 文档进行加载;
  • 但是当文档加载过程中遇到 js 文件,html 文档会挂起渲染(加载解析渲染同步)的线程,不仅要等待文档中 js 文件加载完毕,还要等待解析执行完毕,才可以恢复 html 文档的渲染线程。

加载外联 js 和 css 的阻塞情况,一个不太严谨但方便记忆的口诀:JS 全阻塞,CSS 半阻塞:

  • JS 会阻塞后续 DOM 解析以及其它资源(如 CSS、JS 或图片资源)的加载;
  • CSS 不阻塞 DOM 的加载和解析(它只阻塞 DOM 的渲染呈现,这里谈加载),不会阻塞其它资源(如图片)的加载,但是会阻塞后续 JS 文件的执行(原因之一是,js 执行代码可能会依赖到 css 样式,css 只阻塞执行而不阻塞 js 的加载);
  • 鉴于上面的特性,当 css 后面存在 js 的时候,css 会间接地阻塞 js 后面资源的加载(css 阻塞 js,js 阻塞其他资源)。

现代浏览器会进行 prefetch 优化,浏览器在获得 html 文档之后会对页面上引用的资源进行提前下载。外联 js 文件使用 defer 属性和 async 可以达到异步非阻塞加载的效果,由于现代浏览器都存在 prefetch,所以 defer、async 可能并没有太多的用途,可以作为了解扩展知识,仅仅将脚本文件放到 body 底部(但还是在 </body> 之前)就可以起到很不错的优化效果(遵循先解析再渲染再执行 script 这个顺序)。当把 js 放在最后的时候,其实浏览器将自动忽略 </body> 标签,从而自动在最后的最后补上 </body>

浏览器解析和渲染

步骤如下:

  1. 解析 html,生成 dom 树;
  2. 解析 css,生成 cssom 树;
  3. 将 dom 树和 cssom 树合并,生成渲染树;
  4. 遍历渲染树,开始布局和计算;
  5. 绘制渲染树,显示到屏幕。

1. 解析 html,生成 dom 树

当浏览器接收到服务器响应来的 HTML 文档后,会自上而下扫描文档,开始解析,遍历文档节点,生成 DOM 树。整个构建过程其实包括:字节 -> 字符 -> 令牌 -> 节点对象 -> 对象模型。下面是示例代码:

<html>
<head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link href="style.css" rel="stylesheet">
    <title>Critical Path</title>
</head>
<body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg"></div>
</body>
</html>

2. 解析 css,生成 cssom 树

每个 css 文件都被分析成一个 stylesheet 对象,每个对象都包含 CSS 规则。css 规则对象包含对应于 css 语法的选择器和声明对象以及其他对象。构建过程没有什么特别的差别,下面是示例代码:

body { font-size: 16px }
p { font-weight: bold }
span { color: red }
p span { display: none }
img { float: right }

3. 将 dom 树和 cssom 树合并,生成渲染树

浏览器会先从 dom 树的根节点开始遍历每个可见节点,找到其适配的 CSS 样式规则并应用。将 dom 树与 cssom 树结合在一起,这就是渲染树。

  • 每一个渲染对象都对应着 dom 节点,但是非视觉(隐藏,不占位)dom 元素不会插入渲染树,如 <head> 元素或声明 display: none; 的元素;
  • 渲染对象与 dom 节点不是简单的一对一的关系:一个 dom 可以对应一个渲染对象,但一个 dom 元素也可能对应多个渲染对象,因为有很多元素不止包含一个 css 盒子(如当文本被折行时,会产生多个行盒,这些行会生成多个渲染对象;又如行内元素同时包含块元素和行内元素,则会创建一个匿名块级盒包含内部行内元素,此时一个 dom 对应多个渲染对象)。

4. 遍历渲染树,开始布局和计算

布局阶段会从渲染树的根节点开始遍历,然后确定每个节点对象在页面上的确切大小与位置。布局阶段的输出是一个盒子模型,它会精确地捕获每个元素在屏幕内的确切位置与大小,所有相对的测量值也都会被转换为屏幕内的绝对像素值。

5. 绘制渲染树,显示到屏幕

在绘制阶段,浏览器会立即发出 Paint Setup 与 Paint 事件,开始将渲染树绘制成像素,绘制所需的时间跟 CSS 样式的复杂度成正比,绘制完成后,用户就可以看到页面的最终呈现效果了。

Repaint 和 Reflow

当用户在浏览网页时进行交互或通过 js 脚本改变页面结构时,以上的部分操作有可能重复运行,此过程称为 Repaint 或 Reflow。

  • Repaint:当元素改变的时候,将不会影响元素在页面当中的位置(比如 background-color、border-color、visibility),浏览器仅仅会应用新的样式重绘此元素,此过程称为 Repaint。
  • Reflow:当元素改变的时候,将会影响文档内容或结构,或元素位置,此过程称为 Reflow(HTML 使用的是 flow based layout,也就是流式布局,所以,如果某元件的几何尺寸发生了变化,需要重新布局,也就叫 Reflow)。

Reflow 的成本比 Repaint 的成本高得多的多,我们应当尽量避免 Reflow。

进而:如何优化浏览器渲染过程

针对 html

  • html 文档结构层次尽量少,最好不深于 6 层;
  • 首屏 html 可以少量,主体结构动态插入;
  • 尽量减少将 DOM 节点属性值放在循环当中,会导致大量读写此属性值;
  • 创建有效的 HTML 和 CSS,不要忘记指定文档编码,比如 <meta charset="utf-8">

针对 css

  • 使用媒体查询,减少初次 cssom 树的构建量;
  • 尽量用 id 和 class,不要过度层叠;
  • 样式结构层次尽量简单;
  • 尽可能地为产生动画的 HTML 元素使用 fixed 或 absolute 的 position,那么修改他们的 CSS 是不会 Reflow 的。

针对 js

  • 使用 defer 和 async,避免对文档的阻塞;
  • 可以的话,动态插入 js,避免阻塞;
  • 不要通过 JS 逐条修改 DOM 的样式,提前定义好 CSS 的 Class 进行操作。

针对引入位置

  • css 放到 head,让 cssom 树先行构建;js 放到 </body> 前,保证 dom 树先行构建,不被阻塞;
  • 避免 js 文件的插入跟在 css 文件之后,避免 css 解析对 js 执行的延迟,造成阻塞。

针对资源载入

  • 对页面资源进行压缩,对传输进行 gzip 压缩;
  • 利用 link 标签的 rel 属性进行预解析,运用 http 缓存。

参考文章

系列导航

← JHipster 开发 09:安全体系(下)——授权 目录 开发安全 009:摘要算法 →
← 返回文章列表