在前端开发中我们常常需要考虑首屏加载时间,为了尽可能减少首屏加载时间我们需要弄清楚从输入网址到页面最终呈现的过程中都发生了哪些事情,然后才能具体问题具体分析,最终达到提升网页性能的目的。
从输入网址到页面呈现这个过程大致可分为以下这几个部分:
网络通信
页面渲染
网络通信
输入网址
当我们在浏览器的地址栏输入网址例如(http://www.baidu.com),http://代表使用超文本传输协议,www.baidu.com代表服务器地址,baidu.com代表域名。一个完整的URL包括协议、服务器地址(主机)、端口、路径
负责域名查询与解析的DNS服务
用户通常使用主机名或域名来访问某网站,而不是直接通过IP来访问,因为字母数字配合的表示形式更符合人类的记忆习惯,可计算机却不理解这些名称,因此DNS服务应运而生,DNS协议提供通过域名查找IP地址,或逆向从IP地址反查域名的服务。
DNS查询过程如下:
1.操作系统会先检查本地的hosts文件是否有这个网址映射关系,如果有,就先调用这个IP地址映射,完成域名解析。
2.如果hosts里没有这个域名的映射,则查找本地DNS解析器缓存,是否有这个网址映射关系,如果有,直接返回,完成域名解析。
3.如果hosts与本地DNS解析器缓存都没有相应的网址映射关系,首先会找TCP/IP参数中设置的首选DNS服务器,在此我们叫它本地DNS服务器,此服务器收到查询时,如果要查询的域名,包含在本地配置区域资源中,则返回解析结果给客户机,完成域名解析,此解析具有权威性。
4.如果要查询的域名,不由本地DNS服务器区域解析,但该服务器已缓存了此网址映射关系,则调用这个IP地址映射,完成域名解析,此解析不具有权威性。
5.如果本地DNS服务器本地区域文件与缓存解析都失效,则根据本地DNS服务器的设置(是否设置转发器)进行查询,如果未用转发模式,本地DNS就把请求发至135台根DNS,根DNS服务器收到请求后会判断这个域名(.com)是谁来授权管理,并会返回一个负责该顶级域名服务器的一个IP。本地DNS服务器收到IP信息后,将会联系负责.com域的这台服务器。这台负责.com域的服务器收到请求后,如果自己无法解析,它就会找一个管理.com域的下一级DNS服务器地址(baidu.com)给本地DNS服务器。当本地DNS服务器收到这个地址后,就会找baidu.com域服务器,重复上面的动作,进行查询,直至找到www.baidu.com主机。
6.如果用的是转发模式,此DNS服务器就会把请求转发至上一级DNS服务器,由上一级服务器进行解析,上一级服务器如果不能解析,或找根DNS或把转请求转至上上级,以此循环。不管是本地DNS服务器用是是转发,还是根提示,最后都是把结果返回给本地DNS服务器,由此DNS服务器再返回给客户机。
从客户端到本地DNS服务器是属于递归查询,而DNS服务器之间就是的交互查询就是迭代查询。
应用层 客户端发送HTTP请求报文
HTTP报文包括:
报文首部 (请求行+各种首部字段+其他)
空行
报文主体 (应被发送的数据)通常并不一定要有报文主体
详细HTTP可查看:https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
传输层 确保传输报文可靠性的TCP协议
位于传输层的TCP协议为传输报文提供可靠的字节流服务。为了方便传输,将大块的数据分割成以报文段为单位的数据包进行管理,并为它们编号,方便服务器接收时能准确地还原报文信息。TCP协议通过“三次握手”等方法保证传输的安全可靠。
“三次握手”的过程是,发送端先发送一个带有SYN(synchronize)标志的数据包给接收端,在一定的延迟时间内等待接收的回复。接收端收到数据包后,传回一个带有SYN/ACK标志的数据包以示传达确认信息。接收方收到后再发送一个带有ACK标志的数据包给接收端以示握手成功。在这个过程中,如果发送端在规定延迟时间内没有收到回复则默认接收方没有收到请求,而再次发送,直到收到回复为止。
详细过程如下图
![](https://img.haomeiwen.com/i6079494/74c92574882ccd47.png)
网络层 负责传输的IP协议
IP协议的作用是把TCP分割好的各种数据包传送给接收方。而要保证确实能传到接收方还需要接收方的MAC地址,也就是物理地址。IP地址和MAC地址是一一对应的关系,一个网络设备的IP地址可以更换,但是MAC地址一般是固定不变的。ARP协议可以将IP地址解析成对应的MAC地址。当通信的双方不在同一个局域网时,需要多次中转才能到达最终的目标,在中转的过程中需要通过下一个中转站的MAC地址来搜索下一个中转目标。具体过程如下图:
![](https://img.haomeiwen.com/i6079494/cabfe37f57d07cd7.png)
链路层 传输数据的硬件部分
在网络层找到对方的MAC地址后,就将数据发送到数据链路层传输。至此请求报文已发出,客户端发送请求的阶段结束
服务器接收报文
接收端服务器在链路层接收到数据后,删除该层的首部信息并向网络层传递,网络层将接收的数据向传输层传递,在传输层会将传输的数据按序号从组请求报文并传送给应用层。当数据传输到应用层才能算真正接收到由客户端发送过来的HTTP请求
应用层 服务器发送HTTP响应报文
下面对百度首页响应报文首部进行分析:
状态行
协议版本 状态码 状态码原因短语
HTTP/1.1 200 OK
首部字段
当前服务器上安装的HTTP服务器程序信息
bfe:Baidu Front End。百度人自己写的反向代理及防攻击接入层
Server:bfe/1.0.8.18
响应日期时间
Date:Thu, 08 Dec 2016 14:48:19 GMT
说明报文实体的媒体类型
Content-Type:text/html; charset=utf-8
传输编码方式:分块编码
Transfer-Encoding:chunked
链接方式:持久链接 http/1.1之后这个已经没必要了
Connection:keep-alive
只接受对持相同自然语言的请求返回缓存
Vary:Accept-Encoding
缓存控制:仅向特定用户返回响应
Cache-Control:private
Cxy_all:baidu+43a6e396a3ed26dc7d1de13c6af79e49
缓存过期时间
Expires:Thu, 08 Dec 2016 14:47:38 GMT
X-Powered-By:HPHP
X-UA-Compatible:IE=Edge,chrome=1
Strict-Transport-Security:max-age=172800
BDPAGETYPE:1
BDQID:0xc9d964a600018bb8
BDUSERID:0
设置cookie
Set-Cookie:H_PS_PSSID=1451_21116_17001_21408_21417_21554_20929; path=/;
响应报文的传输方式与请求报文相同,简单点说就是原路返回
网络通信流程图
![](https://img.haomeiwen.com/i6079494/b3580d2b1bbffe04.png)
在网络通信阶段对前端优化建议:
1.减少HTTP请求数
2.合并资源,如合并 JavaScript 文件、CSS 文件,利用 CSS Sprite 合并图片等
3.内联图片,data url节省了HTTP请求,但是如果这个图像在网页多个地方显示会加大网页的内容,延长下载时间。
4.域名提前解析,在页面中不同域名的链接需指定预取域名:http://this-is-a.com">,IE9+支持
5.避免重定向(重定向会增加http请求的次数)
6.cookie优化,cookie越多会导致请求头越大
7.启用GZIP压缩(Accept-Encoding:g-zip)
8.使用 CDN加速,减小服务器压力
9.合理利用HTTP缓存,通过设置Expires
页面渲染
客户端在接收到html代码之后,接下来的流程如下:
解析html以构建DOM树
解析一个文档即将其转换为具有一定意义的结构(编码可以理解和使用的东西)。解析的结果通常是表达文档结构的节点树,称为解析树或语法树。
解析是以文档所遵循的语法规则(编写文档所用的语言或格式)为基础的。所有可以解析的格式都必须对应确定的语法(由词汇和语法规则构成)。这称为与上下文无关的语法。
解析的过程可以分成两个子过程:词法分析和语法分析。
词法分析是将输入内容分割成大量标记的过程。标记是语言中的词汇,即构成内容的单位。在人类语言中,它相当于语言字典中的单词。
语法分析是应用语言的语法规则的过程。
解析器通常将解析工作分给以下两个组件来处理:
词法分析器(有时也称为标记生成器),负责将输入内容分解成一个个有效标记;
而解析器负责根据语言的语法规则分析文档的结构,从而构建解析树。
由于不能使用常规的解析技术,浏览器就创建了自定义的解析器来解析 HTML。此解析算法由两个阶段组成:标记化和树构建。
具体的解析过程可参考浏览器的工作原理中的标记化算法和构建树算法
解析器的输出“解析树”是由 DOM 元素和属性节点构成的树结构。DOM 是文档对象模型 (Document Object Model) 的缩写。它是 HTML 文档的对象表示,同时也是外部内容(例如 JavaScript)与 HTML 元素之间的接口。解析树的根节点是“Document”对象。
当解析到link标签时会请求相应的CSS文件,并将其CSS规则解析为StyleSheet对象,CSS文件中的其他外链资源如背景图片等只有等到其规则与DOM树某节点相匹配时才会加载
当解析遇到img标签时会根据路径向服务器相应的资源文件夹中请求图片资源,但并不会等待图片资源下载完再去解析接下来的html,而是并发执行即图片资源仍在下载,html解析也在进行。如果没有定义图片的height和width属性,那么浏览器为了能够显示每一个加载的图像,它需要先下载图像,然后解析出图像的高度和宽度,并在显示窗口留出相应的屏幕空间,这样就会导致浏览器不断地重新计算/调整页面的布局,这可能会延迟文档的显示,并导致页面重绘。
当解析遇到script标签时,将启动 JavaScript 引擎,这时将阻塞 DOM 树的构建。因为 JavaScript 执行过程中, JavaScript 很可能会对 DOM 树进行读写操作。直到 JavaScript 执行完毕(此时执行的是全局对象初始创建和全局上下文中代码的执行),DOM树才会恢复构建。
构建render树
为了更好地用户体验效果,浏览器会在构建DOM树的同时,也在构建render树。呈现树的每一个节点即为与其相对应的DOM节点的CSS框,框的类型与DOM节点的display属性有关,block元素生成block框,inline元素生成inline框。每一个呈现树节点都有与之相对应的DOM节点,但DOM节点不一定有与之相对应的呈现树节点,比如display属性为none的DOM节点,而且呈现树节点在呈现树中的位置与他们在DOM树中的位置不一定相同,比如float与绝对定位元素。在构建render树的时候需要为DOM树匹配CSS规则,在这个阶段因为匹配规则是从右往左匹配的,所以css的编写规则很重要。不好的CSS选择器写法会影响到页面渲染的效率,具体是如何编写高效的CSS规则的可参考这篇文章CSS选择器性能分析
布局render树
在创建render树时,并不包含位置和大小信息。计算这些值的过程称为布局或重排。布局是一个递归的过程,它从根元素开始,然后递归遍历部分或所有的框架层次结构,为每一个需要计算的呈现器计算几何信息。
布局通常具有以下模式:
父呈现器确定自己的宽度。
父呈现器依次处理子呈现器,并且:
放置子呈现器(设置 x,y 坐标)。
如果有必要,调用子呈现器的布局,这会计算子呈现器的高度。
父呈现器根据子呈现器的累加高度以及边距和补白的高度来设置自身高度,此值也可供父呈现器的父呈现器使用。
将其 dirty 位设置为 false
绘制render树
在绘制阶段,系统会遍历render树,并调用呈现器的“paint”方法,将呈现器的内容显示在屏幕上。绘制工作是使用用户界面基础组件完成的。和布局一样,绘制也分为全局(绘制整个呈现树)和增量两种。在增量绘制中,部分呈现器发生了更改,但是不会影响整个树。更改后的呈现器将其在屏幕上对应的矩形区域设为无效,这导致 OS 将其视为一块“dirty 区域”,并生成“paint”事件。
绘制顺序:
1.背景颜色
2.背景图片
3.边框
4.子代
5.轮廓
6.页面变化造成的影响
在发生变化时,浏览器会尽可能做出最小的响应。因此,元素的颜色改变后,只会对该元素进行重绘。元素的位置改变后,只会对该元素及其子元素(可能还有同级元素)进行布局和重绘。添加 DOM 节点后,会对该节点进行布局和重绘。一些重大变化(例如增大“html”元素的字体)会导致缓存无效,使得整个呈现树都会进行重新布局和绘制。
这里的浏览器渲染原理写的比较粗略,详细请看这篇文章:
下面补充一点其他相关:
1.hosts文件
为了方便用户记忆,我们将IP变成一个个的域名来输入到浏览器进行访问。而这使得访问网站时要先将其域名解析成 IP 。DNS (Domain Name Server) 的作用就是进行 IP 解析,把域名对应到 IP。
在 Great FireWall 的 5 种封锁方法中,有一种简单而效果很好的方法是 DNS 污染。GFW 会对 DNS 的解析过程进行干扰,这会使对某些被干扰的域名返回一个错误的 IP 地址给你的主机,使你无法正确连接到你要的服务器上读取正确的信息。
Hosts 文件本来是用来提高解析效率。在进行 DNS 请求以前,系统会先检查自己的 Hosts 文件中是否有这个地址映射关系,如果有则调用这个 IP 地址映射,如果没有再向已知的 DNS 服务器提出域名解析。也就是说 Hosts 的请求级别比 DNS 高。当你的 Hosts 文件里面有对应的 IP 时,它就会直接访问那个 IP,而不用通过 DNS。
所以,当我们直接将 Google、Twitter、Facebook 之类的 IP 放入 Hosts 文件后,就可以跳过 DNS 的解析这一步,直接就行 IP 访问,不受 GFW 的 DNS 污染干扰了。
补充一条,就是为什么 Hosts 的 IP 要时不时更改,为什么 FB、Twitter 会仍旧上不去。是因为 GFW 的第二个大招,IP 封锁。比如访问国外一个 IP 无法访问,Ping 不通,tracert 这个 IP 后发现,全部在边缘路由器 (GFW) 附近被拦截。换言之,GFW 直接拦截带有这个 IP 头的数据包。所以,如果你更改的 IP 被封锁了,就算你过了 DNS 这一关,也仍旧不能翻过 GFW。
2.ARP解析MAC地址需要了解的:
以太网环境下,同一个网段的主机之间需要互相知道对方的MAC地址,才能访问。
TCP/IP协议栈从上层到下层的封装过程中,第三层封装需要知道目的IP,第二层封装需要知道目的MAC。
目的IP一般由用户手工输入,或者由应用程序填充,也可以通过名称解析系统解析得到,而目的MAC就需要使用ARP来解析。
ARP解析MAC地址的过程:
第一步:
上层应用产生数据,这里用FTP协议为例,在FTP协议中定位了目的IP。
第二步:
那么,封装的过程如下:
应用层:需要FTP的控制信息,包括用户名、密码等;
传输层:目的端口号为21,源为随机端口号;
网络层:目的IP为172.16.1.200,源IP为172.16.1.1;
数据链路层:因为不知道目的IP 172.16.1.200对应的MAC,所以目的IP到目的MAC的封装映射失败;
三层到二层的封装失败,由于二层是以太网,ARP的工作机制便会产生ARP Request去解析目的MAC,此时,源MAC为数据发起者的MAC,目的MAC地址为FFFF:FFFF:FFFF(代表所有MAC)
第三步:
ARP Request到达本网段中的所有设备上,因为目的为FFFF:FFFF:FFFF,所以所有设备都可以拆掉二层的封装,然后解读ARP数据包中需要解析的目的IP。
第四步:
目的IP不正确的设备直接忽略这个ARP请求包,目的IP正确的设备,会产生一个ARP Reply去回应这个ARP Request。
此时,二层的源MAC为被解析设备的MAC,目的为ARP解析发起者的MAC。
第五步:
数据的发起者接到ARP Reply后,将目的IP与目的MAC的对应关系添加到自己的ARP表中。
第六步:
之前未完成二层封装的FTP数据,这时重新开始封装二层头部,此时,正确的目的MAC就被封装到了整个数据帧中。
只有完成了整个TCP/IP协议栈封装的数据帧,才能正常的从主机上发出去。
这就是ARP解析MAC地址的整个过程。
ARP欺骗:https://baike.baidu.com/item/ARP%E6%AC%BA%E9%AA%97/2805503?fr=aladdin
ARP攻击:baike.baidu.com/item/ARP%E6%94%BB%E5%87%BB
因能力有限资历尚浅,不当之处还请指正~
网友评论