大体上,可以分为六步,当然每一步都可以详细展开来说
- 浏览器向 DNS 服务器请求解析该 URL 中的域名所对应的 IP 地址;
- 解析出 IP 地址后,根据该 IP 地址和默认端口 80,和服务器建立TCP连接
- 浏览器发出读取文件(URL 中域名后面部分对应的文件)的HTTP 请求,该请求报文作为 TCP 三次握手的第三个报文的数据发送给服务器;
- 服务器对浏览器请求作出响应,并把对应的 html 文本发送给浏览器;
- 浏览器接受响应
- 渲染页面;
一.DNS域名解析
位于应用层的协议。它提供域名到 IP 地址之间的解析服务。
DNS 解析首先会从你的浏览器的缓存中去寻找是否有这个网址对应的 IP 地址,会有一个递归查找的过程,从浏览器 -> 系统 -> 路由器 -> ISP,这个过程中任何一步找到了都会结束查找流程。
如果都没有找到的话,就会向 ISP 或者公共的域名解析服务发起 DNS 查找请求
1.1 检查浏览器是否有缓存
1. 通过Cache-Control和Expires来检查是否命中强缓存,命中则直接取本地磁盘的html(状态码为200 from disk(or memory) cache,内存or磁盘);
2. 如果没有命中强缓存,则会向服务器发起请求(先进行下一步的TCP连接),服务器通过Etag和Last-Modify来与服务器确认返回的响应是否被更改(协商缓存),若无更改则返回状态码(304 Not Modified),浏览器取本地缓存;
3. 若强缓存和协商缓存都没有命中则返回请求结果。
二.与 WEB 服务器建立 TCP 连接。
2.1 TCP 位于传输层,提供可靠的字节流服务,(字节流服务:为了方便传输,将大块数据分割成以报文段(segment)为单位的数据包进行管理)
确保数据能到达目标,TCP 协议采用了三次握手,连接建立,可以开始数据传输了。
为什么是3次? :避免历史连接,确认客户端发来的请求是这次通信的人。
为什么不是4次? :3次够了第四次浪费
三. 发送HTTP请求/https请求
HTTPS = HTTP + 加密 + 认证 + 完整性保护
HTTP的端口为80/8080,而HTTPS的端口为443
3.1. http协议是基于TCP/IP协议之上的应用层协议。
- 无状态保存
HTTP 协议自身不具备保存之前发送过的请求或响应的功能,每当有新的请求发送时,就会有对应的新响应生成。协议本身并不保留之前一切的请求或响应报文的信息, ( 但为了实现期望的保持状态功能,于是引入了 Cookie 技术 )
用HTTP协议,每当有新的请求发送时,就会有对应的新响应产 生。协议本身并不保留之前一切的请求或响应报文的信息。这是为了更快地处理大量事务,确保协议的可伸缩性,而特意把HTTP协议设计成 如此简单的。可是,随着Web的不断发展,因无状态而导致业务处理变得棘手 的情况增多了。比如,用户登录到一家购物网站,即使他跳转到该站的 其他页面后,也需要能继续保持登录状态。针对这个实例,网站为了能 够掌握是谁送出的请求,需要保存用户的状,HTTP/1.1虽然是无状态协议,但为了实现期望的保持状态功能, 于是引入了Cookie技术。有了Cookie再用HTTP协议通信,就可以管理状态了。
(1)使用 Cookie 的状态管理
缺点: 假设要求登录认证的 Web 页面本身无法进行状态的管理(不记录已登录的状态),那么每次跳转新页面不是要再次登录,就是要在每次请求报文中附加参数来管理登录状态
优点: 由于不必保存状态,自然可减少服务器的 CPU 及内存资源的消耗
3.2 http请求格式
发送HTTP请求的过程就是构建HTTP请求报文并通过TCP协议中发送到服务器指定端
请求报文: 请求报文是由请求方法、请求 URI、协议版本、可选的请求首部字段和内容实体构成的,如下图所示
3.3. HTTP 请求方法
- GET :获取资源
- POST:传输实体主体,POST 的主要目的并不是获取响应的主体内容
- PUT:传输文件,HTTP/1.1 的 PUT 方法自身不带验证机制,任何人都可以上传文件 , 存在安全性问题,一般的 Web 网站不使用该方法。
响应的意思其实是请求执行成功了,但无数据返回
- HEAD:获得报文首部,用于确认,URI 的有效性及资源更新的日期时间等,和 GET 一样,但不返回报文主体
- OPTIONS:询问支持的方法,OPTIONS 方法用来查询针对请求 URI 指定的资源支持的方法
- TRACE:追踪路径
3.4.http缓存
HTTP属于客户端缓存,我们常认为浏览器有一个缓存数据库,用来保存一些静态文件,缓存规则分为强制缓存和协商缓存下面我们分为以下几个方面来简单介绍HTTP缓存
强制缓存
浏览器不会向服务器发送任何请求,直接从本地缓存中读取文件并返回Status Code: 200 OK
对于强制缓存,服务器响应的header中会用两个字段来表明——Expires和Cache-Control。使用chrome的开发者工具,可以很明显的看到对于强制缓存生效时,网络请求的情况
Expires
Expires的值为服务端返回的到期时间,即下一次请求时,请求时间小于服务端返回的到期时间,直接使用缓存数据。
不过Expires 是HTTP 1.0的东西,现在默认浏览器均默认使用HTTP 1.1,所以它的作用基本忽略。另一个问题是,到期时间是由服务端生成的,但是客户端时间可能跟服务端时间有误差,这就会导致缓存命中的误差。
所以HTTP 1.1 的版本,使用Cache-Control替代。
cache-control是http1.1的头字段,expires是http1.0的头字段,如果expires和cache-control同时存在,cache-control会覆盖expires,建议两个都写。可以兼容浏览器
Cache-Control
Cache-Control 是最重要的规则。常见的取值有private、public、no-cache、max-age,no-store,默认为private。
| Cache-Control取值 | 解释 |
|---|---|
| public | 当指定使用 public 指令时,则明确表明其他用户也可利用缓存 |
| private | 响应只以特定的用户作为对象,这与 public指令的行为相反 |
| no-cache | 需要使用对比缓存来验证缓存数据 |
| no-store | 该指令规定缓存不能在本地存储请求或响应的任一部分 |
| max-age=xxx | 缓存的内容将在 xxx 秒后失效 |
| min-fresh=60(单位:秒) | 指令要求缓存服务器返回至少还未过指定时间的缓存资源 |
| only-if-cached | 表示客户端仅在缓存服务器本地缓存目标资源的情况下才会要求其返回。换言之,该指令要求缓存服务器不重新加载响应,也不会再次确认资源有效性。若发生请求缓存服务器的本地缓存无响应,则返回状态码 504 Gateway Timeout |
| must-revalidate | 代理会向源服务器再次验证即将返回的响应缓存目前是否仍然有效。若代理无法连通源服务器再次获取有效资源的话,缓存必须给客户端一条 504(Gateway Timeout)状态码 |
| proxy-revalidate | 令要求所有的缓存服务器在接收到客户端带有该指令的请求返回响应之前,必须再次验证缓存的有效性 |
| no-transform | 规定无论是在请求还是响应中,缓存都不能改变实体主体的媒体类型。这样做可防止缓存或代理压缩图片等类似操作。 |
举个例子:
图中Cache-Control仅指定了max-age,所以默认为private,缓存时间为31536000秒(365天)
也就是说,在365天内再次请求这条数据,都会直接获取缓存数据库中的数据,直接使用。
协商缓存
向服务器发送请求,服务器会根据这个请求的request header的一些参数来判断是否命中协商缓存,如果命中,则返回304状态码并带上新的response header通知浏览器从缓存中读取资源;如下图,协商缓存就是对比缓存
举个例子:
第一次访问:
再次访问:
通过两图的对比,我们可以很清楚的发现,在协商缓存生效时,状态码为304,并且报文大小和请求时间大大减少。
原因是,服务端在进行标识比较后,只返回header部分,通过状态码通知客户端使用缓存,不再需要将报文主体部分返回给客户端。
对于协商缓存来说,缓存标识的传递是我们着重需要理解的,它在请求header和响应header间进行传递,
一共分为两种标识传递,接下来,我们继续介绍
-
Last-Modified / If-Modified-Since (成对出现的,呈一一对应关系)
-
Last-Modified:
服务器在响应请求时,告诉浏览器资源的最后修改时间。
- If-Modified-Since:
再次请求服务器时,通过此字段通知服务器上次请求时,服务器返回的资源最后修改时间。
服务器收到请求后发现有头If-Modified-Since 则与被请求资源的最后修改时间进行比对。
若资源的最后修改时间大于If-Modified-Since,说明资源又被改动过,则响应整片资源内容,返回状态码200;
若资源的最后修改时间小于或等于If-Modified-Since,说明资源无新修改,则响应HTTP 304,告知浏览器继续使用所保存的cache。
-
Etag / If-None-Match(优先级高于Last-Modified / If-Modified-Since)
-
Etag:
服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器决定)。
- If-None-Match:
再次请求服务器时,通过此字段通知服务器客户段缓存数据的唯一标识。
服务器收到请求后发现有头If-None-Match 则与被请求资源的唯一标识进行比对,
不同,说明资源又被改动过,则响应整片资源内容,返回状态码200;
相同,说明资源无新修改,则响应HTTP 304,告知浏览器继续使用所保存的cache。
两者区别:
- Last-Modifed/If-Modified-Since的时间精度是秒,而Etag可以更精确。
- Etag优先级是高于Last-Modifed的,所以服务器会优先验证Etag
- Last-Modifed/If-Modified-Since是http1.0的头字段
总结:
强制缓存,服务器通知浏览器一个缓存时间,在缓存时间内,下次请求,直接用缓存,不在时间内,执行比较缓存策略。
协商缓存,将缓存信息中的Etag和Last-Modified通过请求发送给服务器,由服务器校验,返回304状态码时,浏览器直接使用缓存。
四. 服务器对浏览器请求作出响应并返回HTTP报文;
它会对TCP连接进行处理,对HTTP协议进行解析,并按照报文格式进一步封装成HTTP Request对象,供上层使用。这一部分工作一般是由服务器去进行
4.1. 响应报文: 响应报文基本上由协议版本、状态码(表示请求成功或失败的数字代码)、用以解释状态码的原因短语、可选的响应首部字段以及实体主体构成
4.2. HTTP状态码
所有HTTP响应的第一行都是状态行,依次是当前HTTP版本号,3位数字组成的状态代码,以及描述状态的短语,彼此由空格分隔。
状态代码的第一个数字代表当前响应的类型:
代表性几个状态码:
- 200 OK: 表示从客户端发来的请求在服务器端被正常处理了
- 204 No Content: 一般在只需要从客户端往服务器发送信息,而对客户端不需要发送新信息内容的情况下使用
- 206 Partial Content : 表示客户端进行了范围请求,而服务器成功执行了这部分的GET 请求。响应报文中包含由 Content-Range 指定范围的实体内容。
- 301 Moved Permanently
- 302 Found 该状态码表示请求的资源已被分配了新的 URI,希望用户(本次)能使用新的 URI 访问。
- 303 See Other:于请求对应的资源存在着另一个 URI,应使用 GET方法定向获取请求的资源
- 307 Temporary Redirect:临时重定向
- 400 Bad Request:该状态码表示请求报文中存在语法错误。当错误发生时,需修改请求的内容后再次发送请求。另外,浏览器会像 200 OK 一样对待该状态码
- 401 Unauthorized:该状态码表示发送的请求需要有通过 HTTP 认证(BASIC 认证、DIGEST 认证)的认证信息。另外若之前已进行过 1 次请求,则表示用 户认证失败。
-403 Forbidden:该状态码表明对请求资源的访问被服务器拒绝了
- 404 Not Found:该状态码表明服务器上无法找到请求的资源
- 500 Internal Server Error:该状态码表明服务器端在执行请求时发生了错误
- 503 Service Unavailable:表明服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。如果事先得知解除以上状况需要的时间,最好写入RetryAfter 首部字段再返回给客户端
五:浏览器接受响应
浏览器接收到来自服务器的响应资源后,会对资源进行分析。
首先查看 Response header,根据不同状态码做不同的事
如果响应资源进行了压缩(比如 gzip),还需要进行解压。
然后,对响应资源做缓存。
接下来,根据响应资源里的 MIME 类型去解析响应内容(比如 HTML、Image各有不同的解析方式)。
六:渲染页面
浏览器内核
- 解析HTML形成DOM树
- 解析CSS形成CSSOM 树
- 合并DOM树和CSSOM树形成渲染树
- 浏览器开始渲染并绘制页面
这个过程涉及两个比较重要的概念回流和重绘,DOM结点都是以盒模型形式存在,需要浏览器去计算位置和宽度等,这个过程就是回流。等到页面的宽高,大小,颜色等属性确定下来后,浏览器开始绘制内容,这个过程叫做重绘。浏览器刚打开页面一定要经过这两个过程的,但是这个过程非常非常非常消耗性能,所以我们应该尽量减少页面的回流和重绘
回流
当Render Tree中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程称为回流。
会导致回流的操作:
- 页面首次渲染
- 浏览器窗口大小发生改变
- 元素尺寸或位置发生改变
- 元素内容变化(文字数量或图片大小等等)
- 元素字体大小变化
- 添加或者删除可见的DOM元素
- 激活CSS伪类(例如::hover)
- 查询某些属性或调用某些方法
重绘
重绘:英文叫repaint,当节点的部分属性发生变化,但不影响布局,只需要重新计算节点在屏幕中的绝对位置并渲染的过程,就叫重绘。比如:改变元素的背景颜色、字体颜色等操作会造成重绘。
回流的过程在重绘的过程前面,所以回流一定会重绘,但重绘不一定会引起回流。
容易造成重绘操作的css:
- color
- border-style
- border-radius
- text-decoration
- box-shadow
- outline
- background
每次回流都会对浏览器造成额外的计算消耗,所以浏览器对于回流和重绘有一定的优化机制。浏览器通常都会将多次回流操作放入一个队列中,等过了一段时间或操作达到了一定的临界值,然后才会挨个执行,这样能节省一些计算消耗。但是在获取布局信息操作的时候,会强制将队列清空,也就是强制回流,比如访问或操作以下或方法时:
- offsetTop
- offsetLeft
- offsetWidth
- offsetHeight
- scrollTop
- scrollLeft
- scrollWidth
- scrollHeight
- clientTop
- clientLeft
- clientWidth
- clientHeight
- getComputedStyle()
这些属性或方法都需要得到最新的布局信息,所以浏览器必须去回流执行。因此,在项目中,尽量避免使用上述属性或方法,如果非要使用的时候,也尽量将值缓存起来,而不是一直获取。
减少回流和重绘
合并样式修改
减少造成回流的次数,如果要给一个节点操作多个css属性,而每一个都会造成回流的话,尽量将多次操作合并成一个 方法一:使用style的cssText:
oDiv.style.cssText = 'padding:5px; border:1px solid #000; margin:5px;';
方法二:将这几个样式定义给一个类名,然后给标签添加类名:
<style>
.pbm{
padding:5px;
border:1px solid #000;
margin:5px;
}
</style>
<script>
var oDiv = document.querySelector('.box');
oDiv.classList.add('pbm');
</script>
批量操作DOM
当对DOM有多次操作的时候,需要使用一些特殊处理减少触发回流,其实就是对DOM的多次操作,在脱离标准流后,对元素进行的多次操作,不会触发回流,等操作完成后,再将元素放回标准流。
脱离标准流的操作有以下3中:
- 隐藏元素
- 使用文档碎片
- 拷贝节点
避免多次触发布局
对于页面中比较复杂的动画,尽量将元素设置为绝对定位,操作元素的定位属性,这样只有这一个元素会回流,如果不是定位的话,容易引起其父元素以及子元素的回流。
七:浏览器解析执行js脚本
八:浏览器发起网络请求(ajax,axios)
九:服务器响应ajax请求
-
ajax请求在到达真正的server之前,可能还会经过网关全线校验、消息队列或nginx等负载均衡处理
-
到达server后,后端会解析http请求报文,得到url、请求参数、http头、cookie等等信息
-
登录校验、权限校验(cookie校验、jwt权限校验等)
-
可能会查询数据库,进行常用的CRUD(增删改查)等操作
-
返回响应数据
十:浏览器处理事件循环等异步逻辑。
setTimeout、setInterval、Promise等宏任务、微任务队列