Open bibi7 opened 4 years ago
终于轮到这个了,做一次知识点的串联,部分写过的就直接贴链接
可能你输入的www.baidu.com是你很经常访问的一个页面,那么这个时候浏览器可能会帮你自动补全,且在你点击回车的时候会检查你本次输入的url的协议,是http或者https,因为我们一般输入是不会带上端口的,http的话默认端口是80,https默认端口为443
www.baidu.com
浏览器会根据解析出得协议,开辟一个网络线程,前往请求资源
// 请求方法是GET,路径为根路径,HTTP协议版本为1.1 GET / HTTP/1.1
相关文章
你以为我要讲dns?其实是缓存哒! 浏览器有很多种缓存,比如强缓存和协商缓存。在发起请求之前会先检查当前url是否是已缓存的?而这一步往往就是强缓存返回的东西。
强缓存主要控制的字段有expires和cache control,同时后者比前者的优先级更高,根据不同的http版本。后者是1.1中才支持的字段 Expires即过期时间,存在于服务端返回的响应头中,告诉浏览器在这个过期时间之前可以直接从缓存里面获取数据,无需再次请求。
Expires
Expires: Wed, 22 Nov 2019 08:41:00 GMT //表示资源在2019年11月22号8点41分过期,过期了就得向服务端发请求。
看上去好像没啥问题,但是要注意的一点是,服务器的时间和本地浏览器的时间很可能并不能保持一致。后续在1.1的版本中放弃了这个字段。
和expires有着本质上的区别,cache control并不会用一个具体的时间来表述,转而采用的是采用字段的形式。比如max-age: 3600代表的就是该资源一小时内可用,下次可以直接使用缓存。 更多的相关字段,比如:
expires
cache control
max-age: 3600
public
private
no-cache
no-store
你输入的网址是www.baidu.com,但是浏览器其实只认识ip,怎么让浏览器通过一串域名获取到真正存放资源的服务器的ip地址?所以这中间其实涉及到一个dns解析的问题。 有这么几个步骤:
dns查询又可分为递归查询和迭代查询两种方式:
递归查询:如果主机所询问的本地域名服务器不知道被查询的域名的IP地址,那么本地域名服务器就以DNS客户的身份,向其它根域名服务器继续发出查询请求报文(即替主机继续查询),而不是让主机自己进行下一步查询。 迭代查询:当根域名服务器收到本地域名服务器发出的迭代查询请求报文时,要么给出所要查询的IP地址,要么告诉本地服务器:“你下一步应当向哪一个域名服务器进行查询”。然后让本地服务器进行后续的查询。这种转发的操作可能持续多次,最终会指向真正的资源地址。
递归查询:如果主机所询问的本地域名服务器不知道被查询的域名的IP地址,那么本地域名服务器就以DNS客户的身份,向其它根域名服务器继续发出查询请求报文(即替主机继续查询),而不是让主机自己进行下一步查询。
迭代查询:当根域名服务器收到本地域名服务器发出的迭代查询请求报文时,要么给出所要查询的IP地址,要么告诉本地服务器:“你下一步应当向哪一个域名服务器进行查询”。然后让本地服务器进行后续的查询。这种转发的操作可能持续多次,最终会指向真正的资源地址。
所以一次完整的dns查询往往是递归查询和迭代查询并存的方式:
在通过第一步的DNS域名解析后,获取到了服务器的IP地址,在获取到IP地址后,便会开始建立一次连接,这是由TCP协议完成的,主要通过三次握手进行连接。 第一次握手: 建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认; 第二次握手: 服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态; 第三次握手: 客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。 完成三次握手,客户端与服务器开始传送数据。
目的是 “为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误” 谢希仁版《计算机网络》中的例子是这样的,“已失效的连接请求报文段” 的产生在这样一种情况下:client 发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达 server。本来这是一个早已失效的报文段。但 server 收到此失效的连接请求报文段后,就误认为是 client 再次发出的一个新的连接请求。于是就向 client 发出确认报文段,同意建立连接。假设不采用 “三次握手”,那么只要 server 发出确认,新的连接就建立了。由于现在 client 并没有发出建立连接的请求,因此不会理睬 server 的确认,也不会向 server 发送数据。但 server 却以为新的运输连接已经建立,并一直等待 client 发来数据。这样,server 的很多资源就白白浪费掉了。采用 “三次握手” 的办法可以防止上述现象发生。例如刚才那种情况,client 不会向 server 的确认发出确认。server 由于收不到确认,就知道 client 并没有要求建立连接。” 如果你细读RFC793,也就是 TCP 的协议 RFC,你就会发现里面就讲到了为什么三次握手是必须的——TCP 需要 seq 序列号来做可靠重传或接收,而避免连接复用时无法分辨出 seq 是延迟或者是旧链接的 seq,因此需要三次握手来约定确定双方的 ISN(初始 seq 序列号)
目的是 “为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误”
谢希仁版《计算机网络》中的例子是这样的,“已失效的连接请求报文段” 的产生在这样一种情况下:client 发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达 server。本来这是一个早已失效的报文段。但 server 收到此失效的连接请求报文段后,就误认为是 client 再次发出的一个新的连接请求。于是就向 client 发出确认报文段,同意建立连接。假设不采用 “三次握手”,那么只要 server 发出确认,新的连接就建立了。由于现在 client 并没有发出建立连接的请求,因此不会理睬 server 的确认,也不会向 server 发送数据。但 server 却以为新的运输连接已经建立,并一直等待 client 发来数据。这样,server 的很多资源就白白浪费掉了。采用 “三次握手” 的办法可以防止上述现象发生。例如刚才那种情况,client 不会向 server 的确认发出确认。server 由于收不到确认,就知道 client 并没有要求建立连接。”
如果你细读RFC793,也就是 TCP 的协议 RFC,你就会发现里面就讲到了为什么三次握手是必须的——TCP 需要 seq 序列号来做可靠重传或接收,而避免连接复用时无法分辨出 seq 是延迟或者是旧链接的 seq,因此需要三次握手来约定确定双方的 ISN(初始 seq 序列号)
构建HTTP请求报文并通过TCP协议中发送到服务器指定端口,http(80/8080),https(443) 一个http请求有三个部分组成:请求行, 请求报头和请求正文。
请求行包含请求方法、URL、协议版本
eg: GET index.html HTTP/1.1
请求报头 2.1 host:主机名 2.2 Content-Length:表示请求消息正文的长度。 2.3 Cookie:携带的cookie 2.4 Referer:用户发出请求时的url地址 2.5 User-Agent:浏览器类型,往往可以用来判断浏览器版本等其他信息 2.6 Connection:是否需要持久连接。http1.1中一般是keep-alive 2.7 accept:浏览器可接受的MIME类型,比如text/html, application/xhtml+xml, application/xml等 2.8 method:GET or POST or 其他请求方法 2.9 If-Modified-Since / If-None-Match:协商缓存需要使用到的字段 本来是想写点常用的报头,结果发现都挺常用的。。。 另外常用的请求方法:GET、POST、PUSH、DELETE、OPTIONS、HEAD、PATCH
text/html
application/xhtml+xml
application/xml
请求正文body。
既然都说到了请求方法,也可以讲点这方面的东西,顺便cookie就不另外开一个段落了
get和post虽说是不同的请求方法,也会有很多人会研究他们之间的差别,比如:
从传参的角度来说,GET请求的数据会附在URL之后,以?分割URL和传输数据,参数之间以&相连,而post往往把数据放在body之中。
从安全性的角度来说,post比get更安全(其实也没多安全)。
GET在浏览器回退时是无害的,而POST会再次提交请求。
GET请求在URL中传送的参数是有长度限制的,而POST没有(浏览器的限制,而不是get的限制)。
对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
get是幂等的,post是非幂等的
虽然MDN,确实写了get不允许带body,但是再来看看RCF的http协议的规范:see here
Request message framing is independent of method semantics, even if the method does not define any use for a message body.
所以从协议本身来说,RFC并没有如此规定,但是Get 不用body是W3C的标准,所以mdn和rfc在这点上表现不一致也情有可原。
从最本质的区别来看: GET用于获取指定的资源(幂等) POST用于创建/更新某个资源 (非幂等)
讨论这点的时候,到底是谈的specification 还是 implementation,specification 说白了就是RFC
还是会的,但是对于client来说必须发起的请求必须带有Expect: 100-continue请求头,如果server通过了校验,则回复“100 - Continue”,客户端再把剩下的数据发给服务器。
Expect: 100-continue
对于非简单跨域请求而言,会先发起一次预检请求,请求方法为OPTIONS
在原本三次握手的基础上增加了https四次握手
see here
第一次挥手:数据传输结束以后,客户端的应用进程发出连接释放报文段,并停止发送数据,其首部:FIN=1,seq=u。
第二次挥手:服务器端收到连接释放报文段之后,发出确认报文,其首部:ack=u+1,seq=v。此时本次连接就进入了半关闭状态,客户端不再向服务器发送数据。而服务器端仍会继续发送。
第三次挥手:若服务器已经没有要向客户端发送的数据,其应用进程就通知服务器释放TCP连接。这个阶段服务器所发出的最后一个报文的首部应为:FIN=1,ACK=1,seq=w,ack=u+1。
第四次挥手:客户端收到连接释放报文段之后,必须发出确认:ACK=1,seq=u+1,ack=w+1。进入TIME_AWAIT状态,再经过2MSL(最长报文端寿命)后,本次TCP连接真正结束,通信双方完成了他们的告别。
为什么需要2MSL? 虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。在Client发送出最后的ACK回复,但该ACK可能丢失。Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。
完成了网络请求部分,当浏览器接收到一个html资源,就是要去渲染这些资源,都说第一步是构建dom树,请问怎么构建呢?
HTML5 解析算法分为两个阶段:
标记化算法 这个算法输入为HTML文本,输出为HTML标记,也成为标记生成器。其中运用有限自动状态机来完成。即在当当前状态下,接收一个或多个字符,就会更新到下一个状态。
<html> <body> Hello sanyuan </body> </html>
通过一个简单的例子来演示一下标记化的过程。
遇到<, 状态为标记打开。
<
接收[a-z]的字符,会进入标记名称状态。
[a-z]
这个状态一直保持,直到遇到>,表示标记名称记录完成,这时候变为数据状态。
>
接下来遇到body标签做同样的处理。
body
这个时候html和body的标记都记录好了。
html
现在来到<body>中的>,进入数据状态,之后保持这样状态接收后面的字符hello sanyuan。
<body>
hello sanyuan
接着接收 </body> 中的<,回到标记打开, 接收下一个/后,这时候会创建一个end tag的token。
</body>
end tag
token
随后进入标记名称状态, 遇到>回到数据状态。
接着以同样的样式处理 </body>。
建树算法
DOM 树是一个以document为根节点的多叉树。因此解析器首先会创建一个document对象。标记生成器会把每个标记的信息发送给建树器。建树器接收到相应的标记时,会创建对应的 DOM 对象。
document
document对象
更详细的标记算法和建树过程可参考:see here
关于CSS样式,它的来源一般是三种:
浏览器是无法直接识别 CSS 样式文本的,因此渲染引擎接收到 CSS 文本之后第一件事情就是将其转化为一个结构化的对象,即styleSheets,控制台能够通过document.styleSheets来查看这个最终的结构。
主要就是两个规则: 继承和层叠。 在计算完样式之后,所有的样式值会被挂在到window.getComputedStyle当中
合并dom tree和css tree为render tree,跟着计算出实际在浏览器中展示的的宽高,颜色,一般也称为Layout(Webkit)或者Reflow(Mozilla)。最终将完整的布局树渲染出来
构建 DOM 树:浏览器将 HTML 解析成树形结构的 DOM 树,一般来说,这个过程发生在页面初次加载,或页面 JavaScript 修改了节点结构的时候。
构建渲染树:浏览器将 CSS 解析成树形结构的 CSSOM 树,再和 DOM 树合并成渲染树。
布局(Layout):浏览器根据渲染树所体现的节点、各个节点的CSS定义以及它们的从属关系,计算出每个节点在屏幕中的位置。Web 页面中元素的布局是相对的,在页面元素位置、大小发生变化,往往会导致其他节点联动,需要重新计算布局,这时候的布局过程一般被称为回流(Reflow)。
绘制(Paint):遍历渲染树,调用渲染器的 paint() 方法在屏幕上绘制出节点内容,本质上是一个像素填充的过程。这个过程也出现于回流或一些不影响布局的 CSS 修改引起的屏幕局部重画,这时候它被称为重绘(Repaint)。实际上,绘制过程是在多个层上完成的,这些层我们称为渲染层(RenderLayer)。
渲染层合成(Composite):多个绘制后的渲染层按照恰当的重叠顺序进行合并,而后生成位图,最终通过显卡展示到屏幕上。
这一部分没怎么接触过,感觉有几篇写的挺好的: 说一说从输入URL到页面呈现发生了什么?——渲染过程篇 浏览器渲染流程&Composite(渲染层合并)简单总结
回顾一下渲染流水线的流程:
简单来说,就是当我们对 DOM 结构的修改引发 DOM 几何尺寸变化的时候,会发生回流的过程。 具体一点,有以下的操作会触发回流:
一个 DOM 元素的几何属性变化,常见的几何属性有width、height、padding、margin、left、top、border 等等, 这个很好理解。
使 DOM 节点发生增减或者移动。
读写 offset族、scroll族和client族属性的时候,浏览器为了获取这些值,需要进行回流操作。
调用 window.getComputedStyle 方法。
视口浏览器是尺寸发生变化
依照上面的渲染流水线,触发回流的时候,如果 DOM 结构发生改变,则重新渲染 DOM 树,然后将后面的流程(包括主线程之外的任务)全部走一遍。
当 DOM 的修改导致了样式的变化,并且没有影响几何属性的时候,会导致重绘(repaint)。 由于没有导致 DOM 几何属性的变化,因此元素的位置信息不需要更新,从而省去布局的过程。流程如下:
比如利用 CSS3 的transform、opacity、filter这些属性就可以实现合成的效果,也就是大家常说的GPU加速。 在合成的情况下,会直接跳过布局和绘制流程,直接进入非主线程处理的部分,即直接交给合成线程处理。主要用来发挥gpu加速,可以更流畅的展示。具体可见
dns部分: 浅析dns缓存 DNS递归查询与迭代查询 从输入URL到页面加载的过程?如何由一道题完善自己的前端知识体系! get,post部分: http中的get和post的区别是什么呢?
终于轮到这个了,做一次知识点的串联,部分写过的就直接贴链接
当我们输入了url
可能你输入的
www.baidu.com
是你很经常访问的一个页面,那么这个时候浏览器可能会帮你自动补全,且在你点击回车的时候会检查你本次输入的url的协议,是http或者https,因为我们一般输入是不会带上端口的,http的话默认端口是80,https默认端口为443浏览器会根据解析出得协议,开辟一个网络线程,前往请求资源
构建请求行
缓存
你以为我要讲dns?其实是缓存哒! 浏览器有很多种缓存,比如强缓存和协商缓存。在发起请求之前会先检查当前url是否是已缓存的?而这一步往往就是强缓存返回的东西。
expires(http1)
强缓存主要控制的字段有expires和cache control,同时后者比前者的优先级更高,根据不同的http版本。后者是1.1中才支持的字段
Expires
即过期时间,存在于服务端返回的响应头中,告诉浏览器在这个过期时间之前可以直接从缓存里面获取数据,无需再次请求。看上去好像没啥问题,但是要注意的一点是,服务器的时间和本地浏览器的时间很可能并不能保持一致。后续在1.1的版本中放弃了这个字段。
cache control(http1.1)
和
expires
有着本质上的区别,cache control
并不会用一个具体的时间来表述,转而采用的是采用字段的形式。比如max-age: 3600
代表的就是该资源一小时内可用,下次可以直接使用缓存。 更多的相关字段,比如:public
: 不光是客户端,沿途的cdn都可缓存private
:只允许客户端缓存no-cache
:不使用强制缓存no-store
:不适用任何形式缓存dns解析
你输入的网址是
www.baidu.com
,但是浏览器其实只认识ip,怎么让浏览器通过一串域名获取到真正存放资源的服务器的ip地址?所以这中间其实涉及到一个dns解析的问题。 有这么几个步骤:解析过程
dns查询又可分为递归查询和迭代查询两种方式:
所以一次完整的dns查询往往是递归查询和迭代查询并存的方式:
TCP链接
在通过第一步的DNS域名解析后,获取到了服务器的IP地址,在获取到IP地址后,便会开始建立一次连接,这是由TCP协议完成的,主要通过三次握手进行连接。 第一次握手: 建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认; 第二次握手: 服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态; 第三次握手: 客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。 完成三次握手,客户端与服务器开始传送数据。
为什么需要三次握手而不是两次或者四次?
发送http请求
构建HTTP请求报文并通过TCP协议中发送到服务器指定端口,http(80/8080),https(443) 一个http请求有三个部分组成:请求行, 请求报头和请求正文。
请求行包含请求方法、URL、协议版本
请求报头 2.1 host:主机名 2.2 Content-Length:表示请求消息正文的长度。 2.3 Cookie:携带的cookie 2.4 Referer:用户发出请求时的url地址 2.5 User-Agent:浏览器类型,往往可以用来判断浏览器版本等其他信息 2.6 Connection:是否需要持久连接。http1.1中一般是keep-alive 2.7 accept:浏览器可接受的MIME类型,比如
text/html
,application/xhtml+xml
,application/xml
等 2.8 method:GET or POST or 其他请求方法 2.9 If-Modified-Since / If-None-Match:协商缓存需要使用到的字段 本来是想写点常用的报头,结果发现都挺常用的。。。 另外常用的请求方法:GET、POST、PUSH、DELETE、OPTIONS、HEAD、PATCH请求正文body。
get?post?cookie?options?
既然都说到了请求方法,也可以讲点这方面的东西,顺便cookie就不另外开一个段落了
get和post虽说是不同的请求方法,也会有很多人会研究他们之间的差别,比如:
从传参的角度来说,GET请求的数据会附在URL之后,以?分割URL和传输数据,参数之间以&相连,而post往往把数据放在body之中。
从安全性的角度来说,post比get更安全(其实也没多安全)。
GET在浏览器回退时是无害的,而POST会再次提交请求。
GET请求在URL中传送的参数是有长度限制的,而POST没有(浏览器的限制,而不是get的限制)。
对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
get是幂等的,post是非幂等的
get就一定没有body吗?
虽然MDN,确实写了get不允许带body,但是再来看看RCF的http协议的规范:see here
所以从协议本身来说,RFC并没有如此规定,但是Get 不用body是W3C的标准,所以mdn和rfc在这点上表现不一致也情有可原。
从最本质的区别来看: GET用于获取指定的资源(幂等) POST用于创建/更新某个资源 (非幂等)
对于post发起两次包
还是会的,但是对于client来说必须发起的请求必须带有
Expect: 100-continue
请求头,如果server通过了校验,则回复“100 - Continue”,客户端再把剩下的数据发给服务器。options
对于非简单跨域请求而言,会先发起一次预检请求,请求方法为OPTIONS
对于http2来说
如果是https呢?
在原本三次握手的基础上增加了https四次握手
TCP四次挥手
第一次挥手:数据传输结束以后,客户端的应用进程发出连接释放报文段,并停止发送数据,其首部:FIN=1,seq=u。
第二次挥手:服务器端收到连接释放报文段之后,发出确认报文,其首部:ack=u+1,seq=v。此时本次连接就进入了半关闭状态,客户端不再向服务器发送数据。而服务器端仍会继续发送。
第三次挥手:若服务器已经没有要向客户端发送的数据,其应用进程就通知服务器释放TCP连接。这个阶段服务器所发出的最后一个报文的首部应为:FIN=1,ACK=1,seq=w,ack=u+1。
第四次挥手:客户端收到连接释放报文段之后,必须发出确认:ACK=1,seq=u+1,ack=w+1。进入TIME_AWAIT状态,再经过2MSL(最长报文端寿命)后,本次TCP连接真正结束,通信双方完成了他们的告别。
为什么需要2MSL? 虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。在Client发送出最后的ACK回复,但该ACK可能丢失。Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。
浏览器渲染:构建dom树
完成了网络请求部分,当浏览器接收到一个html资源,就是要去渲染这些资源,都说第一步是构建dom树,请问怎么构建呢?
HTML5 解析算法分为两个阶段:
标记化算法 这个算法输入为HTML文本,输出为HTML标记,也成为标记生成器。其中运用有限自动状态机来完成。即在当当前状态下,接收一个或多个字符,就会更新到下一个状态。
通过一个简单的例子来演示一下标记化的过程。
遇到
<
, 状态为标记打开。接收
[a-z]
的字符,会进入标记名称状态。这个状态一直保持,直到遇到
>
,表示标记名称记录完成,这时候变为数据状态。接下来遇到
body
标签做同样的处理。这个时候
html
和body
的标记都记录好了。现在来到
<body>
中的>
,进入数据状态,之后保持这样状态接收后面的字符hello sanyuan
。接着接收
</body>
中的<
,回到标记打开, 接收下一个/后,这时候会创建一个end tag
的token
。随后进入标记名称状态, 遇到
>
回到数据状态。接着以同样的样式处理
</body>
。建树算法
DOM 树是一个以
document
为根节点的多叉树。因此解析器首先会创建一个document对象
。标记生成器会把每个标记的信息发送给建树器。建树器接收到相应的标记时,会创建对应的 DOM 对象。更详细的标记算法和建树过程可参考:see here
浏览器渲染:样式计算
关于CSS样式,它的来源一般是三种:
格式化样式表
浏览器是无法直接识别 CSS 样式文本的,因此渲染引擎接收到 CSS 文本之后第一件事情就是将其转化为一个结构化的对象,即styleSheets,控制台能够通过document.styleSheets来查看这个最终的结构。
计算每个节点的具体样式
主要就是两个规则: 继承和层叠。 在计算完样式之后,所有的样式值会被挂在到window.getComputedStyle当中
布局树(or render tree)
合并dom tree和css tree为render tree,跟着计算出实际在浏览器中展示的的宽高,颜色,一般也称为Layout(Webkit)或者Reflow(Mozilla)。最终将完整的布局树渲染出来
小结
构建 DOM 树:浏览器将 HTML 解析成树形结构的 DOM 树,一般来说,这个过程发生在页面初次加载,或页面 JavaScript 修改了节点结构的时候。
构建渲染树:浏览器将 CSS 解析成树形结构的 CSSOM 树,再和 DOM 树合并成渲染树。
布局(Layout):浏览器根据渲染树所体现的节点、各个节点的CSS定义以及它们的从属关系,计算出每个节点在屏幕中的位置。Web 页面中元素的布局是相对的,在页面元素位置、大小发生变化,往往会导致其他节点联动,需要重新计算布局,这时候的布局过程一般被称为回流(Reflow)。
绘制(Paint):遍历渲染树,调用渲染器的 paint() 方法在屏幕上绘制出节点内容,本质上是一个像素填充的过程。这个过程也出现于回流或一些不影响布局的 CSS 修改引起的屏幕局部重画,这时候它被称为重绘(Repaint)。实际上,绘制过程是在多个层上完成的,这些层我们称为渲染层(RenderLayer)。
渲染层合成(Composite):多个绘制后的渲染层按照恰当的重叠顺序进行合并,而后生成位图,最终通过显卡展示到屏幕上。
从布局树到真正渲染落地
这一部分没怎么接触过,感觉有几篇写的挺好的: 说一说从输入URL到页面呈现发生了什么?——渲染过程篇 浏览器渲染流程&Composite(渲染层合并)简单总结
是不是可以谈到重绘和重排?
回顾一下渲染流水线的流程:
重排or回流
简单来说,就是当我们对 DOM 结构的修改引发 DOM 几何尺寸变化的时候,会发生回流的过程。 具体一点,有以下的操作会触发回流:
一个 DOM 元素的几何属性变化,常见的几何属性有width、height、padding、margin、left、top、border 等等, 这个很好理解。
使 DOM 节点发生增减或者移动。
读写 offset族、scroll族和client族属性的时候,浏览器为了获取这些值,需要进行回流操作。
调用 window.getComputedStyle 方法。
视口浏览器是尺寸发生变化
依照上面的渲染流水线,触发回流的时候,如果 DOM 结构发生改变,则重新渲染 DOM 树,然后将后面的流程(包括主线程之外的任务)全部走一遍。
重绘
当 DOM 的修改导致了样式的变化,并且没有影响几何属性的时候,会导致重绘(repaint)。 由于没有导致 DOM 几何属性的变化,因此元素的位置信息不需要更新,从而省去布局的过程。流程如下:
合成
比如利用 CSS3 的transform、opacity、filter这些属性就可以实现合成的效果,也就是大家常说的GPU加速。 在合成的情况下,会直接跳过布局和绘制流程,直接进入非主线程处理的部分,即直接交给合成线程处理。主要用来发挥gpu加速,可以更流畅的展示。具体可见
针对重排的优化建议
外部参考:
dns部分: 浅析dns缓存 DNS递归查询与迭代查询 从输入URL到页面加载的过程?如何由一道题完善自己的前端知识体系! get,post部分: http中的get和post的区别是什么呢?