概述
简单来说,在浏览器输入URL,按下回车键之后,经历了以下几个过程:
- URL解析
- DNS解析: 将域名解析成IP地址
- TCP链接: TCP三次握手
- 发送HTTP请求
- 服务器处理请求并返回HTTP报文
- 浏览器解析渲染页面
- 断开连接:TCP四次握手
URL解析
地址解析
首先判断输入的是一个合法的URL还是一个待搜索的关键词,并且根据输入的内容进行自动完成、字符编码等操作
HSTS
由于安全隐患,会使用HSTS强制客户端使用HTTPS协议访问页面
其他操作
浏览器会进行一些额外的操作,比如安全检车,访问限制等
检查缓存
关于浏览器缓存可以看浏览器缓存机制
DNS查询
因为发送HTTP请求报文需要依赖下层TCP协议,TCP建立连接并通信是需要IP地址+端口号,端口号可以直接从URL中获得,但是IP地址需要DNS解析域名来获得。
DNS服务主要功能就是将URL中的域名解析成IP地址。
基本步骤如下:
- 浏览器缓存: 浏览器会按照一定的频率缓存DNS记录
- 操作系统缓存: 如果浏览器缓存中找不到需要的DNS记录,那就去操作系统的hosts文件中找
- 路由缓存
- ISP DNS: ISP是互联网服务提供商(Internet Service Provider)的简称,ISP有专门的DNS服务器应对DNS查询请求。ISP DNS就是在客户端电脑上设置的首选DNS服务器,他们在大多数情况下都会有缓存
- 根服务器 ISP的DNS服务器还找不到的话,就会向根服务器发出请求,进行递归查询
从客户端到本地服务器属于递归查询,而DNS服务器之间的交互属于迭代查询
注意点:
TCP连接
TCP三次握手
握手目的
- 同步Sequence序列号
- 初始序列号ISN
- 交换TCP通信的一些信息 如: MSS,窗口大小 指定校验和算法
Sequence序列号: 因为TCP需要有确认,并且需要防止上次连接的迟到的报文被接收,所以需要对每个报文打上一个标记,用来防止失序或上一次连接的报文被接收。
握手过程
- 客户端发送一个带
SYN=1 Seq=X的数据包到服务器端口(第一次握手,由浏览器发起,告诉服务器我要发送请求了) - 服务器发回一个带有
SYN=1 ACK=X+1 Seq=Y的响应包以示传达确认信息(第二次握手,由服务器发起,告诉浏览器我准备接受了,你可以发送了) - 客户端再传回一个带
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 | 用于对资源进行部分修改 |
| CONNECT | HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器 |
| TRACE | 回显服务器收到的请求,主要用于测试或诊断 |
- HEAD的一个使用场景是在下载一个大文件前先获取其大小,再决定是否要下载,以此可以节约带宽资源
常考题目:GET和POST的区别
| 区别内容 | GET | POST |
|---|---|---|
| 点击返回/刷新按钮 | 没有影响 | 数据会重新发送(浏览器会提示“数据被重新提交”) |
| 添加书签 | 可以 | 不可以 |
| 缓存 | 可以 | 不可以 |
| 编码类型(Encoding type) | application/x-www-form-rulencoded | application/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协议版本
响应头
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
空行
最后一个响应头之后是一个空行,发送回车、换行符,通知浏览器以下不再有相应头
响应体
服务器返回给客户端的响应信息
浏览器渲染页面
浏览器解析渲染页面可以分为以下步骤
- 根据HTML解析出DOM树
- 根据CSS解析生成CSS规则树
- 结合DOM树和CSS规则树,生成渲染树
- 根据渲染树计算每一个节点的信息
- 根据计算好的信息绘制页面
HTML解析
分为以下四个步骤:
- 解码: 根据二件指定编码转换成字符串,也就是HTML代码
- 预解析:提前加载资源,减少处理时间
- 符号化
- 构建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>前
如果需要改变阻塞模式,可以使用defer和async
参考文章
根据计算好的信息绘制页面
绘制阶段,系统会遍历呈现树,并调用绘制器的paint(),将内容显示在屏幕上,再绘制的过程肿还会出现重绘和回流
重绘、回流
重绘:某个元素的背景颜色,文字颜色等不影响元素周围或内部布局的属性,将会引起浏览器的重绘
回流/重排:某个元素的尺寸发生了变化,则需要重新计算渲染树,重新渲染
回流必将引起重绘,重绘不一定会引起回流
回流比重绘的代价要更高
如何避免回流和重绘(常考题目)
css
- 避免使用
table布局- 由于浏览器用的是流式布局,对渲染树的计算通常只需要遍历一次就可以完成,但是
table及其内部元素除外,他们可能需要多次计算,通常要花三倍于同等元素的时间
- 由于浏览器用的是流式布局,对渲染树的计算通常只需要遍历一次就可以完成,但是
- 尽可能再DOM树的最末端改变class
- 避免设置多层内联样式
- 将动画效果应用到
position:absolute/fixed的元素上 - 避免使用CSS表达式(例如
calc()) - 加载图片的时候,提前写好宽高
js
- 避免频繁操作样式,最好一次性重写
style属性,或者一次性更新class属性 - 避免频繁操作DOM,创建一个documengFragment,再它上面应用所有的DOM操作,然后再添加到文档肿
- 可以先为元素设置
display:none,操作结束后,再显示出来。因为display:none的元素上进行DOM操作不会引发回流和重绘;display:none的节点不会被加入渲染树,而visibility:hidden则会,所以某个节点,最开始是不显示的,设为display:none最佳display:none会触发回流,而visibility:hidden只会触发重绘,因为没有发现位置变化
- 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来
- 对具有复杂动画的元素 使用绝对定位,使它脱离文档流,否则会引起父元素以及后续元素的频繁回流
- resize事件 最好加上防抖(debounce),尽量少触发
断开连接:TCP四次握手
当数据传送完毕,需要断开tcp连接,此时发起tcp四次挥手
- 发起方向被动方发送报文,Fin、Ack、Seq,表示已经没有数据传输了。并进入 FIN_WAIT_1 状态。(第一次挥手:由浏览器发起的,发送给服务器,我请求报文发送完了,你准备关闭吧)
- 被动方发送报文,Ack、Seq,表示同意关闭请求。此时主机发起方进入 FIN_WAIT_2 状态。(第二次挥手:由服务器发起的,告诉浏览器,我请求报文接受完了,我准备关闭了,你也准备吧)
- 被动方向发起方发送报文段,Fin、Ack、Seq,请求关闭连接。并进入 LAST_ACK 状态。(第三次挥手:由服务器发起,告诉浏览器,我响应报文发送完了,你准备关闭吧)
- 发起方向被动方发送报文段,Ack、Seq。然后进入等待 TIME_WAIT 状态。被动方收到发起方的报文段以后关闭连接。发起方等待一定时间未收到回复,则正常关闭。(第四次挥手:由浏览器发起,告诉服务器,我响应报文接受完了,我准备关闭了,你也准备吧)