概述
浏览器,呈现用户所选择的web资源,资源的格式通常是HTML,也可以是PDF、IMAGE及其他方式。这些资源需要从服务器请求,用户通过URI(Uniform Resource Identifier统一资源标识符)来制定所请求资源的位置,然后显示在浏览器窗口中。那么,当我们在浏览器的地址栏输入URI后,到完整地呈现数据这个期间都发生了什么呢?今天,我想通过这个梳理出一个前端的完整的知识体系。
从耗时的角度,浏览器请求、加载、渲染也个页面,时间花在下面五件事情上:
1、DNS查询;
2、TCP连接;
3、HTTP请求即响应;
4、服务器响应;
5、客户端渲染。
一、DNS查询
DNS(Domain Name System域名系统),用于TCP/ICP网络,用来将主机名和域名转化为IP地址的工作,大致流程:
- 如果浏览器有缓存,直接使用浏览器缓存,否则使用本级缓存,再没有就使用host;
- 如果本地没有,就向dns域名服务器查询到对应的IP
由于dns的解析很耗时,当解析域名过多的时候,便会导致首屏加载变得过慢。
二、TCP连接
http是一个基于TCP/IP通信协议进行数据的传输。
TCP(传输控制协议)——应用程序之间通信。当应用程序希望通过TCP与另一个应用程序通信时,它会发送一个通信请求。在双方“握手”之后,TCP将在两个应用程序之间建立通信,直到一方或双方关闭为止。UDP(用户数据包协议,应用程序之间的简单通信)和TCP很相似,但更简单,可靠性也更低。
IP(用户数据包协议)——计算机之间的通信。通过IP,消息或者其他数据被分割为小的独立的包,通过因特网,将每个包路由至目的地。
建立连接的三次握手
客户端:hello,你是server么?
服务端:hello,我是server,你是client么
客户端:yes,我是client
建立连接成功后,接下来就正式传输数据。
断开连接的四次挥手
主动方:我已经关闭了向你那边的主动通道了,只能被动接收了
被动方:收到通道关闭的信息
被动方:那我也告诉你,我这边向你的主动通道也关闭了
主动方:最后收到数据,之后双方无法通信
相信很多面试者会经常被问到http请求方式中get和post的区别。
GET方法:
- 查询字符串(名称/值对)是在 GET 请求的 URL 中发送的;
- GET 请求可被缓存;
- GET 请求保留在浏览器历史记录中;
- GET 请求可被收藏为书签;
- GET 请求不应在处理敏感数据时使用;
- GET 请求有长度限制;
- GET 请求只应当用于取回数据;
POST方法
- 查询字符串(名称/值对)是在 POST 请求的 HTTP 消息主体中发送的;
- POST 请求不会被缓存;
- POST 请求不会保留在浏览器历史记录中;
- POST 不能被收藏为书签;
- POST 请求对数据长度没有要求;
其实以上说的都只是GET、POST在http层面上的区别,在TCP/IP层面也有区别,也是二者真正的区别,即GET会产生一个tcp数据包,post两个。具体就是: - GET请求时,浏览器会把headers和data一起发送,服务器响应200(返回数据);
- POST请求时,浏览器会先发送headers,服务器响应100 continue,浏览器再发送data,服务器响应200(返回数据)。
三、HTTP请求响应
HTTP(HyperText Transfer Protocol超文本传输协议),是基于TCP的应用层协议,是互联网的基础协议。历经了HTTP/0.9,HTTp/1.0,HTTP/1.1,HTTP/2 几个版本。
HTTP/0.9
发布于1991年,它不涉及数据包(package)传输,主要规定了客户端和服务器之间的传输方式,默认使用80端口。该版本只有GET请求方式,服务器只能回应HTML格式的字符串。当服务器发送完毕后,就关闭TCP连接。
HTTP/1.0
发布于1996年5月,增加了许多内容。
- 可以发送任何形式的内容,包括文字、图像、视频、二进制文件等等;
- 除了GET请求方式,还引入了POST和HEAD命令;
- 请求和回应的格式也变了。每次通信都必须包括头信息(HTTP header),回应包含了状态码、缓存、内容编码等。
该版本的缺点是每个TCP连接只能发送一个请求,数据发送完毕,连接就关闭,若还要请求其他资源,就必须再新建一个连接。为解决这个问题,可在请求时,使用Connection: keep-alive
。
HTTP/1.1
发布于1997年1月,进一步完善了HTTP协议,直到现在还是最流行的版本。
1.1版的最大变化,是引入了持久连接,即TCP连接默认不关闭,可以被多个请求复用。不用声明Connection: keep-alive
。当客户端和服务器发现对方一段时间没有活动,就可以主动关闭连接。目前,对于同一个域名,大多数浏览器允许同时建立6个持久连接。此外,还新增了 新增PUT、DELETE、PATCH、OPTIONS方法。
该版本的缺点是,虽然它允许复用TCP连接,但在同一个TCP连接里,所有的数据通信是按次序进行的。服务器只有处理完一个回应,才会进行下一个回应。要是前面的回应特别慢,后面就会有许多请求排队等着,这称为["队头堵塞"](Head-of-line blocking)。为了避免这个问题,只有两种方法:一是减少请求数,二是同时多开持久连接。比如合并脚本和样式表、将图片嵌入CSS代码、域名分片(domain sharding)等。
HTTP/2
发布于2015年,它不叫 HTTP/2.0,是因为标准委员会不打算再发布子版本了,下一个新版本将是 HTTP/3。它有以下的一些特性:
- 多路复用。为了解决HTTP/1.1中对头堵塞的问题,在该版本复用TCP连接时,在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不用按照顺序一一对应。举个例子,在一个TCP连接里面,服务器同时收到了A请求和B请求,先回应A请求,结果发现处理过程非常耗时,于是就发送A请求已经处理好的部分, 接着回应B请求,完成后,再发送A请求剩下的部分。
- 首部压缩。HTTP请求是无状态的,即服务器中没有保存客户端的状态,客户端必须每次带上自己的状态去请求服务器。所以,请求的很多字段都是重复的,比如Cookie和User Agent,一模一样的内容,每次请求都必须附带,这会浪费很多带宽,也影响速度。HTTP/2 对这一点做了优化,引入了头信息压缩机制(header compression)。一方面,头信息使用gzip或compress压缩后再发送;另一方面,客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就提高速度了。
- 二进制分帧。HTTP/2 是一个彻底的二进制协议,头信息和数据体都是二进制,并且统称为"帧"(frame):头信息帧和数据帧。
- 服务器推送。HTTP/2 允许服务器未经请求,主动向客户端发送资源。常见场景是客户端请求一个网页,这个网页里面包含很多静态资源。正常情况下,客户端必须收到网页后,解析HTML源码,发现有静态资源,再发出静态资源请求。其实,服务器可以预期到客户端请求网页后,很可能会再请求静态资源,所以就主动把这些静态资源随着网页一起发给客户端了。
- 请求优先级。如果流被赋予了优先级,它就会基于这个优先级来处理,由服务器决定需要多少资源来处理该请求。
HTTPS
它是安全版的http,相当于ssl+http。每次在请求前,先建立ssl,确保接下来的通信是加密的,无法轻易被截取分析。
四、服务器响应
服务器处理完客户端的请求后要做出响应,很多时候可以通过状态码来判断:
不同范围状态的意义:
- 1xx——指示信息,表示请求已接收,继续处理
- 2xx——成功,表示请求已被成功接收、理解、接受
- 3xx——重定向,要完成请求必须进行更进一步的操作
- 4xx——客户端错误,请求有语法错误或请求无法实现
- 5xx——服务器端错误,服务器未能实现合法的请求
常见状态码: - 200——表明该请求被成功地完成,所请求的资源发送回客户端
- 301 ——请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。
-302 ——临时移动。 - 304——自从上次请求后,请求的网页未修改过,请客户端使用本地缓存
- 400——客户端请求有错(譬如可以是安全模块拦截)
- 401——请求未经授权
- 403——禁止访问(譬如可以是未登录时禁止)
- 404——资源未找到
- 500——服务器内部错误
- 503——服务不可用。服务器目前无法使用
- 504——网关超时。服务器作为网关或代理,但是没有及时从上游服务器收到请求
- 505—— HTTP版本不受支持。服务器不支持请求中所用的 HTTP 协议版本
五、客户端渲染
当下流行的浏览器有Webkit内核 的Chrome、Safari,Geoko内核的Firefox,Trident内核的IE,不同浏览器的内核不同,各个浏览器的渲染引擎的渲染过程也略有不同。今天主要讲述以Webkit为内核的Chrome浏览器的渲染过程。
上图是webkit渲染的主流程。我们可以看到,它大致分为以下五个步骤:
1、处理HTML标记,构建DOM树;
2、处理CSS标记,构建CSSOM树;
3、将DOM树和CSSOM树合并成渲染树;
4、布局渲染树,计算各元素的尺寸、位置;
5、绘制渲染树,绘制页面像素信息。
构建DOM树
DOM是文档对象模型的缩写,它是html文档的对象表示。假设有这样一个HTML页面:
<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>
<imgsrc="awesome-photo.jpg">
</div>
</body>
</html>
最后的DOM树如下:
构建CSSOM树
CSS规则树的生成也是类似。例如有份css文件内容如下:
body {
font-size: 16px
}
p {
font-weight:bold
}
span {
color: red
}
p span {
display:none
}
img {
float: right
}
最终的CSSOM树:
值得注意的是,现代浏览器总是并行加载资源的。默认情况下,CSS是阻塞加载的,但它并不会阻塞DOM树的解析,它阻塞的是DOM的渲染,直至CSSOM树构建完成。因而为了避免看到长时间的白屏,我们尽可能早地提前加载CSS文件。
构建渲染树
一般来说,渲染树和DOM树是相对应的,但有一些不可见的DOM元素不会插入到渲染树中,如head这种不可见的标签或者 display:none
等,所以也不是严格上的一一对应。
绘制渲染树
当我们通过js动态修改了DOM和CSS,会导致重新布局(Layout)或渲染(Repaint)。
注意:Layout和Repaint的概念是有区别的:
Layout,也称为Reflow,即回流。一般意味着元素的内容、结构、位置或尺寸发生了变化,需要重新计算样式和渲染树.
Repaint,即重绘。意味着元素发生的改变只是影响了元素的一些外观之类的时候(例如,背景色,边框颜色,文字颜色等),此时只需要应用新样式绘制这个元素就可以了.
什么会引起回流?
1.页面渲染初始化
2.DOM结构改变,比如删除了某个节点
3.render树变化,比如减少了padding
4.窗口resize
5.最复杂的一种:获取某些属性,引发回流。
回流一定伴随着重绘,重绘却可以单独出现。看以下示例:
var s = document.body.style;
s.padding = "2px"; // 回流+重绘
s.border = "1px solid red"; // 再一次 回流+重绘
s.color = "blue"; // 再一次重绘
s.backgroundColor = "#ccc"; // 再一次 重绘
s.fontSize = "14px"; // 再一次 回流+重绘
// 添加node,再一次 回流+重绘
document.body.appendChild(document.createTextNode('abc!'));
在此我顺便插一句display:none
和visibility:hidden
的区别。虽然二者皆表示隐藏元素,但 visibility
在渲染树上是存在的,它在页面上仍有空间,只是被隐藏了,设置该值后只因起页面重绘;而display:none
,将发生回流、重绘。
六、参考链接
- https://www.oschina.net/news/76365/http-introduce
- https://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651553818&idx=1&sn=3ce840113d28ee2b2cafe4c7fc48ef91&chksm=802557dbb752decd2118e3ad7a3ea803a0c41c6594f539fc54830dae9bbc2242b2fc03e7fb1c&mpshare=1&scene=1&srcid=0322CxWOpTK4CgdmkrNTCAoP&key=5c9df8bf97da386fd7b420dd3411de5f37141e6e8b8055daa86264175d9652388941eb99ffe954f615facf24db09cc951b588d9a65a18fcd3dba7222659a598eeac54368c1e558dcb9a861ccce69a81e&ascene=0&uin=Mjg1NzA1NjAwOA%3D%3D&devicetype=iMac+MacBookAir7%2C2+OSX+OSX+10.12+build(16A323)&version=12020010&nettype=WIFI&lang=zh_CN&fontScale=100&pass_ticket=46vuTi4tTvaGBVezntVk7un797ihzAumK9gTh1d6bjT4k9L6%2BAQ99wCT9sRIIkft