用户输入网址到页面返回都发生了什么?
1、分析地址栏中URL
浏览器做的第一步就是会解析URL得到里面的参数,分析域名是否规范,并将域名和需要的请求的资源分离开来,从而了解需要请求的是哪个服务器,请求的是服务器上的什么资源等等
2、DNS域名解析
浏览器将网址转换成对应的IP地址
-
本地电脑会检查浏览器缓存是否有这个域名解析过的IP地址,如果有,解析完成
-
浏览器缓存中没有的话,就会在操作系统缓存中查找。操作系统会有一个域名解析的过程。Linux中可以通过
/etc/hosts
文件来设置;在Windows中可以通过配置C:WindowsSystem32driversetchosts
文件来设置,用户可以将任何域名解析到任何能够访问的IP地址 -
如果前两种方式都没找到,操作系统会把这个域名发送给这个本地DNS服务器,一个完整的内网通常都会配置本地DNS服务器,一般都会缓存域名解析结果。操作系统和本地域名服务器之间的查询方式是递归查询。
-
本地域名DNS服务器仍然没有命中,就直接到根DNS服务器请求解析。根DNS服务器返回给本地DNS域名服务器一个顶级DNS服务器地址,它是国际顶级域名服务器,如.com、.cn、.org等,全球只有13台左右
本地域名服务器和其他域名服务器之间的查询方式是迭代查询,防止根域名服务器压力过大,通过以下方式进行迭代查询:
- 首先本地域名服务器向根域名服务器发起请求,根域名服务器是最高层次的,它并不会直接指明这个域名对应的 IP 地址,而是返回顶级域名服务器的地址
- 本地域名服务器拿到这个顶级域名服务器的地址后,就向其发起请求,获取权限域名服务器的地址
- 本地域名服务器根据权限域名服务器的地址向其发起请求,最终得到该域名对应的IP地址
注意:
本地域名服务器和其他域名服务器之间的查询方式是迭代查询,防止根域名服务器压力过大
操作系统和本地域名服务器之间的查询方式是递归查询。
也就是说:接受请求的顶级DNS服务器查找并返回此域名对应的Name Server域名服务器的地址,Name Server服务器就是我要访问的网站域名提供商的服务器,其实该域名的解析任务就是由域名提供商的服务器来完成。 (比如访问www.baidu.com,而这个域名是从A公司注册获得的,那么A公司上的服务器就会有www.baidu.com的相关信息)。Name Server服务器会查询存储的域名和IP的映射关系表,再把查询出来的域名和IP地址等等信息,连同一个TTL值返回给本地DNS服务器。
-
本地域名服务器将得到的该域名对应的IP和TTL值返回给操作系统,同时会缓存这个域名和IP的对应关系
-
操作系统将 IP 地址返回给浏览器,同时自己也将IP地址缓存起来
-
至此,浏览器就得到了域名对应的 IP地址,并将IP地址缓存起来
-
域名解析过程结束
DNS域名解析是一个复杂的过程,在实际的DNS解析过程中,可能还不止这些。
3、建立TCP链接
浏览器和服务器之间会简历一个TCP三次握手的连接,用于传输数据。
==TCP通信需要确保双方都具有数据收发的能力,得到ACK响应则认为对方具有数据收发的能力,因此双方都要发送SYN确保对方具有通信的能力。==
- 第一次握手是客户端发送SYN,服务端接收,服务端得出客户端的发送能力和服务端的接收能力都正常
- 第二次握手是服务端发送SYN+ACK,客户端接收,客户端得出客户端发送接收能力正常,服务端发送接收能力也都正常,但是此时服务器并不能确认客户端的接收能力是否正常
- 第三次握手客户端发送ACK,服务器接收,服务端才能得出客户端发送接收能力正常,服务端自己发送接收能力也都正常。
4、HTTP请求
浏览器向服务器发送一个HTTP请求报文,包括请求方法、请求头、请求体等信息。
5、HTTP响应
服务器接受到请求后,根据请求内容进行处理,返回一个HTTP响应报文,包括状态码、响应头、响应体等。
6、渲染页面
浏览器根据响应报文的内容,解析HTML、CSS、JS等资源,构建DOM树、CSSOM树、渲染树,然后进行布局、绘制、合成等步骤,最终显示在页面
7、TCP断开
浏览器和服务器之间断开一个TCP四次挥手的连接,释放资源。
- 客户端发送断开TCP连接请求的FIN 报文,报文中会指定一个序列号
- 服务端会回复客户端发送的TCP断开请求报文,服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 +1 作为 ACK 报文的序列号值,表明已经收到客户端的报文
- 服务端在回复完客户端的TCP断开请求后,不会马上进行TCP连接的断开,服务端会先确保断开前,所有传输到A的数据是否已经传输完毕,一旦确认传输数据完毕,和和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号
- 客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 +1 作为自己 ACK 报文的序列号值
从浏览器收到第一个字节起到页面渲染完成,做了什么?
1、构建DOM树
浏览器根据HTML文档中的内容,创建DOM节点,形成层级关系,构成DOM树
2、构建CSSOM树
浏览器根据CSS样式表中的内容,创建CSS规则,并形成层级关系,构成CSSOM树。
注意:==DOM的解析和CSSOM的解析是一个并行的过程。两者互不影响。两者解析完成之后,会合并生成render tree,之后就是layout(布局)和paint(绘制)阶段,渲染到页面中。==
3、构建渲染树
浏览器将DOM树和CSSOM树结合起来,生成渲染树,可以知道每个节点会应用什么样式的数据结构。这个结合的过程就是遍历整个 DOM 树,然后在 CSSOM 树里查询到匹配的样式。
4、布局
此时已经有了需要渲染的所有节点之间的结构关系及其样式信息。接下来就需要进行页面的布局。通过计算渲染树上每个节点的样式,就能得出来每个元素所占空间的大小和位置。当有了所有元素的大小和位置后,就可以在浏览器的页面区域里去绘制元素的边框了,这个过程就是布局。
这个过程中,浏览器对渲染树进行遍历,将元素间嵌套关系以盒模型的形式写入文档流。
5、绘制
- 构建图层
页面上可能有很多复杂的场景,比如3D变化、页面滚动、使用z-index进行z轴的排序等。所以,为了实现这些效果,渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树 - 绘制图层
在完成图层树的构建之后,渲染引擎会对图层树中的每个图层进行绘制。渲染引擎在绘制图层时,会把一个图层的绘制分成很多绘制指令,然后把这些指令按照顺序组成一个待绘制的列表。
绘制列表只是用来记录绘制顺序和绘制指令的列表,而绘制操作是由渲染引擎中的合成线程来完成的。当图层绘制列表准备好之后,主线程会把该绘制列表提交给合成线程。
注意:合成操作是在合成线程上完成的,所以,在执行合成操作时并不会影响到主线程的执行。
很多情况下,图层可能很大,比如一篇长文章,需要滚动很久才能到底,但是用户只能看到视口的内容,所以没必要把整个图层都绘制出来。因此,合成线程会将图层划分为图块,这些图块的大小通常是 256x256 或者 512x512。合成线程会优先将视口附近的图块生成位图。实际生成位图的操作是在光栅化阶段来执行的,所谓的光栅化就是按照绘制列表中的指令生成图片。
6、显示
当所有的图块都被光栅化之后,合成线程就会生成一个绘制图块的命令,浏览器相关进程收到这个指令之后,就会将其页面内容绘制在内存中,最后将内存显示在屏幕上,这样就完成了页面的绘制。
这个过程可能会受到JavaScript的影响,比如遇到非异步的JS代码,会暂停DOM树和CSSOM树的构建,优先执行JS,以为JS代码可能会修改或者访问DOM和CSSOM
重排和重绘
渲染树是动态构建的,所以,DOM节点和CSS节点的改动都可能会造成渲染树的重新构建。渲染树的改动就会造成页面的重排或者重绘
1、重排
当我们的操作引发了 DOM 树中几何尺寸的变化(改变元素的大小、位置、布局方式等),这时渲染树里有改动的节点和它影响的节点都要重新计算。这个过程就叫做重排,也称为回流。在改动发生时,要重新经历页面渲染的整个流程。
页面首次渲染
浏览器窗口大小变化
元素内容变化
元素尺寸变化
元素字体变化
激活CSS伪类
添加/删除可见的DOM元素
在发生重排时,由于浏览器渲染页面是基于流式布局的,所以会导致周围DOM元素重新排列。
- 全局:从根节点开始对整个渲染树进行重新布局
- 局部:从渲染树的某个部分或者一个对象进行重新布局。
2、重绘
当对 DOM 的修改导致了样式的变化、但未影响其几何属性(比如修改颜色、背景色)时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式(会跳过重排环节),这个过程叫做重绘。简单来说,重绘是由对元素绘制属性的修改引发的。
当我们修改元素绘制属性时,页面布局阶段不会执行,因为并没有引起几何位置的变换,所以就直接进入了绘制阶段,然后执行之后的一系列子阶段。相较于重排操作,重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些。
color
background
visibility
box-shadow
border-radius
当触发重排时,一定会触发重绘,但是重绘不一定会引发重排。
3、重排优化方式
- 尽量使用CSS3动画,因为可以调用GPU进行渲染
- CSS表达式
- 将DOM的多个读操作(或者写操作)放在一起,而不是读写操作穿插着写
- 避免频繁操作DOM,可以创建一个文档片段
documentFragment
,在它上面进行所有的DOM操作,最后再添加到文档中
浏览器针对页面的回流与重绘,进行了自身的优化——渲染队列, 浏览器会将所有的回流、重绘的操作放在一个队列中,当队列中的操作到了一定的数量或者到了一定的时间间隔,浏览器就会对队列进行批处理。这样就会让多次的回流、重绘变成一次回流重绘。