浏览器输入URL到渲染页面过程中发生了什么

301 阅读13分钟

概述

简单来说,在浏览器输入URL,按下回车键之后,经历了以下几个过程:

  1. URL解析
  2. DNS解析: 将域名解析成IP地址
  3. TCP链接: TCP三次握手
  4. 发送HTTP请求
  5. 服务器处理请求并返回HTTP报文
  6. 浏览器解析渲染页面
  7. 断开连接:TCP四次握手

URL解析

地址解析

首先判断输入的是一个合法的URL还是一个待搜索的关键词,并且根据输入的内容进行自动完成、字符编码等操作

HSTS

由于安全隐患,会使用HSTS强制客户端使用HTTPS协议访问页面

其他操作

浏览器会进行一些额外的操作,比如安全检车,访问限制等

检查缓存

关于浏览器缓存可以看浏览器缓存机制

DNS查询

因为发送HTTP请求报文需要依赖下层TCP协议,TCP建立连接并通信是需要IP地址+端口号,端口号可以直接从URL中获得,但是IP地址需要DNS解析域名来获得。

DNS服务主要功能就是将URL中的域名解析成IP地址。

基本步骤如下:

  1. 浏览器缓存: 浏览器会按照一定的频率缓存DNS记录
  2. 操作系统缓存: 如果浏览器缓存中找不到需要的DNS记录,那就去操作系统的hosts文件中找
  3. 路由缓存
  4. ISP DNS: ISP是互联网服务提供商(Internet Service Provider)的简称,ISP有专门的DNS服务器应对DNS查询请求。ISP DNS就是在客户端电脑上设置的首选DNS服务器,他们在大多数情况下都会有缓存
  5. 根服务器 ISP的DNS服务器还找不到的话,就会向根服务器发出请求,进行递归查询

从客户端到本地服务器属于递归查询,而DNS服务器之间的交互属于迭代查询

注意点:

  1. DNS劫持
  2. 前端dns-prefetch优化

TCP连接

TCP三次握手

握手目的

  • 同步Sequence序列号
  • 初始序列号ISN
  • 交换TCP通信的一些信息 如: MSS,窗口大小 指定校验和算法

Sequence序列号: 因为TCP需要有确认,并且需要防止上次连接的迟到的报文被接收,所以需要对每个报文打上一个标记,用来防止失序或上一次连接的报文被接收。

握手过程

  1. 客户端发送一个带SYN=1 Seq=X的数据包到服务器端口(第一次握手,由浏览器发起,告诉服务器我要发送请求了)
  2. 服务器发回一个带有SYN=1 ACK=X+1 Seq=Y的响应包以示传达确认信息(第二次握手,由服务器发起,告诉浏览器我准备接受了,你可以发送了)
  3. 客户端再传回一个带ACK=Y+1 Seq=Z的数据包,代表“握手结束”(第三次握手,由浏览器发起,告诉服务器我马上就要发了)

发送HTTP请求

TCP三次握手结束后,开始发送HTTP请求报文

请求报文由请求行请求头空行请求体四个部分组成

请求行

包含请求方法、URL、协议版本

// 请求方法 请求URL 协议和协议版本
POST /index.html HTTP/2.0

请求方法

请求方法描述
GET通常用于请求服务器发送某些资源
HEAD请求资源的头部信息,并且这些头部与HTTP GET方法请求时返回的一致
OPTIONS用于获取目的资源所支持的通信选项
POST发送数据给服务器
PUT用于新增资源或者使用请求中的有效负载替换目标资源的表现形式
DELETE用于删除指定的资源
PATCH用于对资源进行部分修改
CONNECTHTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器
TRACE回显服务器收到的请求,主要用于测试或诊断
  • HEAD的一个使用场景是在下载一个大文件前先获取其大小,再决定是否要下载,以此可以节约带宽资源

常考题目:GET和POST的区别

区别内容GETPOST
点击返回/刷新按钮没有影响数据会重新发送(浏览器会提示“数据被重新提交”)
添加书签可以不可以
缓存可以不可以
编码类型(Encoding type)application/x-www-form-rulencodedapplication/x-www-form-rulencoded or multipart/form-data
历史记录没有
长度限制没有
数据类型限制只允许ACSII字符类型没有限制,允许二进制数据
安全性查询字符串会显示在地址栏的URL上,不安全 不要用GET请求提交敏感数据因为数据不会显示在地址栏中,也不会缓存在浏览器记录中,所以POST请求比GET请求安全,但也不是最安全的方式,如果需要传送敏感数据,需要使用数据加密

请求头

包含请求的附加信息,由关键字/值对组成,每行一堆,关键字和值用英文冒号:分隔

Host: www.baidu.com
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,id;q=0.4

空行

最后一个请求头之后是一个空行,发送回车符和换行符,通知服务以下不在由请求头

请求体

可以承载多个请求参数的数据,并不是所有请求都具有请求数据

name=123&pw=123

服务器处理请求并返回HTTP报文

服务器处理完请求之后,会返回HTTP响应报文

响应报文由状态行响应头空行响应体组成

状态行

包含HTTP协议版本、状态码、状态码描述、回车/换行符

// HTTP协议版 状态码 状态码描述
HTTP/1.1 200 OK

常见HTTP状态码

  • 1xx: 指示信息-表示请求已经收到,继续处理
    • 100:客户必须继续发出请求
    • 101:客户要求服务器根据请求转换HTTP协议版本
  • 2xx成功,表示请求已经被成功接收
    • 200:成功 - 表示请求正常处理
    • 201:已创建 - 请求成功并且服务器创建了新的资源
    • 202:已接受 - 服务器已接受请求,但尚未处理
    • 204:没有资源 - 请求处理成功,但没有资源返回
    • 206:Partial Content - 对资源的某一部分的请求
  • 3xx重定向,要完成请求必须更进一步的操作
    • 300:多种选择 - 针对请求,服务器可执行多种操作。服务器可根据请求者(user agent)选择一项操作,或提供操作列表供请求者选择
    • 301:永久重定向 -请求的网页已永久移动到新的位置。服务器返回此响应(对GET和HEAD)时,会自动将请求者转到新位置
    • 302:临时重定向 - 。服务器目前从不同位置的网页响应请求,但请求者应该继续使用原有位置来进行以后的请求
    • 304:Not Modified 缓存中读取
  • 4xx客户端错误,请求由语法错误或者请求无法实现
    • 400:错误请求 - 请求报文中存在语法错误
    • 401:未授权 - 需要有通过Http认证的认证信息
    • 403:禁止 - 访问被拒绝
    • 404:无法找到请求资源
  • 5xx服务器错误,服务器未能实现合法的请求
    • 500:服务器内部错误 - 服务器再执行时发生错误
    • 501:尚未实施 - 服务器不能具备完成请求的功能。例如,服务器无法识别请求方法时可能会返回此状态码
    • 502:错误网关 - 服务器作为网关或代理,从上游服务器收到无效响应
    • 503:服务不可用 - 服务器处于超负载或者正在进行停机维护
    • 504:网关超时 - 器作为网关或代理,但是没有及时从上游服务器收到请求
    • 505:HTTP版本不受支持 - 服务器不支持请求中所使用的HTTP协议版本

响应头

HTTP常用头部信息

Server: nginx/1.12.2
Date: Mon, 28 Dec 2020 07:12:11 GMT
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Access-Control-Allow-Origin: https://xxxxx.com
Vary: Origin
Access-Control-Allow-Credentials: true

空行

最后一个响应头之后是一个空行,发送回车、换行符,通知浏览器以下不再有相应头

响应体

服务器返回给客户端的响应信息

浏览器渲染页面

浏览器解析渲染页面可以分为以下步骤

  1. 根据HTML解析出DOM树
  2. 根据CSS解析生成CSS规则树
  3. 结合DOM树和CSS规则树,生成渲染树
  4. 根据渲染树计算每一个节点的信息
  5. 根据计算好的信息绘制页面

HTML解析

分为以下四个步骤:

  1. 解码: 根据二件指定编码转换成字符串,也就是HTML代码
  2. 预解析:提前加载资源,减少处理时间
  3. 符号化
  4. 构建DOM树

HTML解析的过程是边下载边解析的,当整个解析过程完成以后,浏览器会通过DOMContentLoaded事件来通知DOM解析完成

CSS解析

解析到html头部,发现有CSS文件,此时下载CSS,CSS也是一边下载一边解析。

一旦浏览器下载了CSS,CSS解析器就会处理它遇到的任何CSS,根据语法规范解析出所有的CSS并进行标记画,然后得到一个规则表(CSSOM树)

常考题目:为什么css要放在里面

渲染树的构成必须有DOM树和CSSOM树,所以尽快构建CSSOM树是一个重要的优化手段,如果CSS文件放在尾部,那么整个过程就是一个串行的过程,先解析了DOM,再去解析CSS。所以CSS一般放在头部,这样DOM树和CSSOM树的构建是同步进行的

构建渲染树

渲染树是一个DOM树和CSS规则树合并的过程

样式计算

DOM树和CSSOM树有了之后,浏览器开始样式计算,主要是为DOM树上的节点找到对应的样式

构建布局树

主要是胃DOM树上的节点找到页面上对应位置,以及一些display:none元素的隐藏

构建分层树

主要是为了满足滚动条,z-index position这些负责的分层操作

讲分层树图块化

利用光栅找到试图窗口下的对应的位图。主要是因为一个页面可能有几屏那么长,一下渲染出来比较浪费,所以浏览器会找到视图窗口对应的图块,将这部分的图块进行渲染

常考题目: 为什么js文件要放在html最底部

当遇到一个script标签时候,DOM构建会被暂停,直至脚本完成执行,然后继续构建DOM树,这样页面就会一直出现白屏界面

如果JS依赖CSS样式,而它没有被下载和构建时,浏览器就会延迟脚本执行,直至CSS规则被创建

所以我们知道:

  • CSS会阻塞JS执行
  • JS会阻塞后面的DOM解析:

为了避免这种情况,有以下原则:

  • CSS资源排在JS资源前面
  • JS放在HTML最底部,也就是</body>

如果需要改变阻塞模式,可以使用deferasync 参考文章

根据计算好的信息绘制页面

绘制阶段,系统会遍历呈现树,并调用绘制器的paint(),将内容显示在屏幕上,再绘制的过程肿还会出现重绘回流

重绘、回流

重绘:某个元素的背景颜色,文字颜色等不影响元素周围或内部布局的属性,将会引起浏览器的重绘

回流/重排:某个元素的尺寸发生了变化,则需要重新计算渲染树,重新渲染

回流必将引起重绘,重绘不一定会引起回流

回流比重绘的代价要更高

如何避免回流和重绘(常考题目)

css

  1. 避免使用table布局
    • 由于浏览器用的是流式布局,对渲染树的计算通常只需要遍历一次就可以完成,但是table及其内部元素除外,他们可能需要多次计算,通常要花三倍于同等元素的时间
  2. 尽可能再DOM树的最末端改变class
  3. 避免设置多层内联样式
  4. 将动画效果应用到position:absolute/fixed的元素上
  5. 避免使用CSS表达式(例如calc()
  6. 加载图片的时候,提前写好宽高

js

  1. 避免频繁操作样式,最好一次性重写style属性,或者一次性更新class属性
  2. 避免频繁操作DOM,创建一个documengFragment,再它上面应用所有的DOM操作,然后再添加到文档肿
  3. 可以先为元素设置display:none,操作结束后,再显示出来。因为display:none的元素上进行DOM操作不会引发回流和重绘;
    • display:none的节点不会被加入渲染树,而visibility:hidden则会,所以某个节点,最开始是不显示的,设为display:none最佳
    • display:none会触发回流,而visibility:hidden只会触发重绘,因为没有发现位置变化
  4. 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来
  5. 对具有复杂动画的元素 使用绝对定位,使它脱离文档流,否则会引起父元素以及后续元素的频繁回流
  6. resize事件 最好加上防抖(debounce),尽量少触发

重回vs回流

断开连接:TCP四次握手

当数据传送完毕,需要断开tcp连接,此时发起tcp四次挥手

  • 发起方向被动方发送报文,Fin、Ack、Seq,表示已经没有数据传输了。并进入 FIN_WAIT_1 状态。(第一次挥手:由浏览器发起的,发送给服务器,我请求报文发送完了,你准备关闭吧)
  • 被动方发送报文,Ack、Seq,表示同意关闭请求。此时主机发起方进入 FIN_WAIT_2 状态。(第二次挥手:由服务器发起的,告诉浏览器,我请求报文接受完了,我准备关闭了,你也准备吧)
  • 被动方向发起方发送报文段,Fin、Ack、Seq,请求关闭连接。并进入 LAST_ACK 状态。(第三次挥手:由服务器发起,告诉浏览器,我响应报文发送完了,你准备关闭吧)
  • 发起方向被动方发送报文段,Ack、Seq。然后进入等待 TIME_WAIT 状态。被动方收到发起方的报文段以后关闭连接。发起方等待一定时间未收到回复,则正常关闭。(第四次挥手:由浏览器发起,告诉服务器,我响应报文接受完了,我准备关闭了,你也准备吧)

参考链接

在浏览器输入URL回车之后发生了什么

经典面试题:从 URL 输入到页面展现到底发生什么?