浏览器从输入 URL 到页面呈现
大概流程
从在浏览器里面输入地址,按下enter键开始:
- DNS解析
- TCP连接
- 发送HTTP请求
- 服务器响应
- 浏览器解析渲染页面
DNS解析
DNS解析过程就是通过网络查找哪台机器有你需要的资源的过程。
浏览器输入一个域名并不是真正意义上的去查找这个,而是去查找对应域名的IP地址。
域名只是一个别名,方便记忆。
DNS解析过程
- 查找
www.github.com; - 访问客户端DNS缓存:「浏览器缓存 -> 系统缓存(host)-> 路由器缓存」,host可以通过ihost去修改。
- 访问 ISP DNS服务器(ISP,互联网服务提供商,有联通电信移动等。如果你是电信网络,则进入电信的 DNS 缓存服务器,以下简称本地),如果本地服务器有,则直接返回;如果没有,让本地 DNS 服务器去咨询查找。
- 本地去咨询 DNS 根服务器,DNS 根服务器发现是 .com 区域 管理的,告诉本地去咨询它。
- 本地去咨询 .com 顶级域名服务器,.com 顶级域名服务器不太清楚,告诉本地去咨询 github.com 主区域 的服务器。
- 本地去咨询 github.com 主域名服务器,github.com 域服务器查找到对应的 IP 地址,返回给本地。
- 本地服务器通知用户,github.com 对应的 IP 地址,同时缓存这个 IP 地址,下次就直接访问了。
- 查找
TCP连接
- 3 次握手。建立客户端和服务器之间的连接。
- 你在吗?
- 我在
- OK
- 数据传输
- 4次挥手。断开客户端和服务器之间的连接。
- 我要断了
- 我看看我还有没要传的
- 我没有要传的了
- OK
发送HTTP请求
发送http请求的过程就是构建HTTP请求报文,并通过TCP协议发送到服务器指定端口(http协议默认端口80/8080/443)。
http请求报文由3部分组成: 请求行,请求报头,请求正文
- 请求行:常用方法有:GET、POST、PUT等
- 请求报头:允许客户端向服务器传递请求的附加信息和客户端自身的信息。
- 请求正文:需要客户端向服务器传递数据,这些数据就储存在请求正文中。
服务器响应
服务器处理完请求后,会返回HTTP报文。
HTTP响应报文也是由3部分组成:状态码、响应报头、响应报文。
- 状态码: 1xx 指示信息-表示请求已接收;2xx 请求成功-表示请求成功接收并解析;3xx 重定向-表示要完成请求需要更进一步操作;4xx 客户端错误-请求有语法错误或者请求无法实现;5xx:服务端错误-服务端未能实现合法的请求。常见状态码:200(成功)、304(请求内容有缓存,不需要更新)、404(网页或者文件找不到)、500(服务器-后端处理错误)
- 响应报头:常见的响应报头字段 Server、Connection 等。
- 响应报文:服务器返回给浏览器的文本信息,通常 HTML、CSS、JS、图片等文件就放在这一部分。
浏览器解析渲染页面

如上图,浏览器渲染过程:
- 解析HTML,生成DOM树。
- 解析css,生成css规则树(css Rule Tree)
- 将DOM TREE 和 css Rule Tree结合,生成渲染树(Render Tree)
- 从根节点开始,计算每一个元素的大小、位置,给出每个节点所应该出现的屏幕精确坐标,从而得到基于渲染树的 布局渲染树(Layout of the render tree)。
- 遍历渲染树,将每个节点用 UI 渲染引擎来绘制,从而将整棵树绘制到页面上,这个步骤叫 绘制渲染树(Painting the render tree)
在解析过程中,可能会产生回流和重绘:
重绘(repaint): 元素样式的改变不影响布局时,浏览器将使用重绘对元素进行更新,此时由于只需要 UI 层面的重新像素绘制,因此损耗较少。
回流(reflow): 又叫重排(layout)。当元素的尺寸、结构或者触发某些属性时,浏览器会重新渲染页面,称为回流。此时,浏览器需要重新经过计算,计算后还需要重新页面布局,因此是较重的操作。 reflow必定repaint. 如何避免触发回流:
- 【CSS】使用 visibility 替换 display
- 【CSS】避免 table 布局。对于 Render Tree 的计算通常只需要遍历一次就可以完成,但是 table 布局需要计算多次,通常要花 3 倍于等同元素的时间,因此要避免。
- 【JS】避免频繁做 width、height 等会触发回流的操作。
- 【JS】操作 DOM 的时候,如果是添加 DOM 节点,可以将所有节点都在 JS 中操作完毕,再进行渲染(一次性)。
优化
渲染过程碰到 JS 文件怎么处理?
javaScript 的加载、解析和执行会阻塞 DOM 的构建。
也就是说:在构建 DOM 的时候,HTML 解析器如果碰到 JavaScript,那么就会停止构建 DOM,将控制权交给 JavaScript 引擎,等 JavaScript 运行完毕,浏览器再从中断的地方恢复 DOM 构建。
也就是说:首屏渲染越快,就越不应该在首屏的时候加载 JS 文件,这也就是建议将 script 标签放到 body 标签底部,或者给 script 标签添加 defer/async 属性的原因。
为什么操作 DOM 慢
- 涉及 JS 引擎和渲染引擎两个线程间的通信,损耗性能。
- 操作 DOM 可能会重复回流,加剧性能损耗。
本文使用 mdnice 排版