拾遗文档【浏览器输入地址到渲染的过程】

264 阅读8分钟

前言:这两天看了一些关于浏览器渲染的一些资料,趁这会有时间写一下总结

回归主题

浏览器是怎么渲染页面的呢?

天才第一步:DNS解析

从我们输入地址按下回车开始,浏览器就开始工作

每个网址都对应有IP地址,那么多的网址,那由谁去保存网址和IP的映射关系呢?

比如百度对应的就是:202.108.22.5

没错,就是DNS服务器

DNS去解析域名的时候会经历一下几个步骤:

  • 浏览器缓存

这是第一步,浏览器会先检查自身缓存中有没有这个域名对应的IP地址,如果存在了就结束域名解析。如果没有就前往下一步

  • 本地hosts文件

接着就会去找电脑操作系统缓存中有没有对应的解析结果。一般是hosts文件,如果你在该文件设置某个域名对应的IP地址,那么浏览器首先会跳转到这个IP地址。所谓的域名劫持就是通过这个原理,修改用户本地的hosts文件,将域名指定到特定的IP地址。

  • 本地DNS解析器缓存

当上面两步都没有命中域名【翻译成白话就是:没有解析域名】的话,就会到本地DNS服务器进行解析,本地DNS服务器也会先从缓存中进行查找,大部分的域名都会从这得到解析

什么叫做本地服务器? 本地服务器(LDNS:Local DNS Serve),DNS服务是有很多级的,所以更靠近用户的那级服务器就叫做本地DNS服务器.一般在你的城市的某个角落,距离你不会很远,并且这台服务器的性能都很好,一般都会缓存域名解析结果,大约80%的域名解析到这里就完成了.

从这开始分割,上面是递归查询,往下是迭代查询。

递归查询: 客户端像本地DNS发起请求,本地DNS没有找到对应IP就会像其它DNS发起请求,找到结果再返回给客户端,而不是客户端自己去请求其它DNS。举例:老板要秘书去找个好一点的酒店,秘书是刚来的,人生地不熟,于是秘书让司机小王去找,小王找到了告诉秘书,秘书在告诉老板,这叫递归查询

迭代查询: 本地DNS服务器没有找到域名对应的IP,就会向根域名服务器发起请求,根域名服务器会告诉本地DNS服务器返回所查询域的主域名服务器地址,接下来一步一步的由本地DNS服务器去查询。举例:小王去找酒店的时候非常辛苦,他询问自己的朋友小李,小李告诉他小明知道,小王又去找小明,小明跟他说小张知道,小明又去找小张,最后终于找到了,这叫迭代查询

  • 本地DNS服务器

1、如果本地服务器的缓存中仍然没有命中,那本地服务器就会向跟服务器(Root DNS Serve)发起请求。

2、根域名服务器根据本地服务器的请求,返回所查询域的主域名服务器(gTLD Serve)地址

3、本地服务器会向上一步返回的主域名服务器发送请求

4、主域名服务器会返回该域名对应的Name Serve地址

Name Serve? 就是网站注册的域名服务器

5、域名服务器根据映射关系,找到域名对应的IP地址,返回给本地服务器

6、本地服务器缓存这个域名对应的IP

7、本地服务器把IP地址返回给用户,用户根据TTL值缓存到本地系统缓存中,到此域名解析结束

那么上面说的TTL值是什么东西? TTL(Time To Live):域名解析信息在DNS中的存在时间。

举个栗子🌰我申请了一个域名:www.lander.com 当用户访问这个地址的时候,本地服务器发现自己没有这个域名对应的IP,那他就会向跟服务器发送请求,根域名服务器通过上述的12345个步骤,终于知道了这个域名对应的IP为6.6.6.6,然后通过上述的67两个步骤,告诉给了用户。而就在这时候,本地服务器为了下次能快速找到这个域名,就把这个6.6.6.6保存了一段时间,这个时间就叫做TTL。 当用户再次输入这个www.lander.com 这个地址,本地DNS就直接返回6.6.6.6给用户,直到TTL值过期。

建立TCP连接(三次握手)

  • 第一次:客户端 ----> 服务端

建立连接,客户端向服务端发送SYN请求报文

  • 第二次:客户端 <---- 服务端

服务端接受并处理SYN请求报文,再通过SYN和ACK报文发送给客户端

  • 第三次:客户端 ----> 服务端

客户端接收SYN和ACK报文,并向服务端发送ACK报文

到此连接建立成功,证明客户端可以向服务端发送请求

发送http请求

浏览器向服务器发送请求,服务器会解析请求头,如果请求有缓存相关的信息,就会验证缓存的信息是否有效,若无效则重新返回资源,状态码200;如果有效则返回缓存的资源,状态码304

关闭连接(四次挥手)

写在前面:服务端也可以先向客户端发起关闭请求,我这里是以客户端为例

  • 第一次:客户端 ----> 服务端

客户端向服务端发送FIN报文,此时客户端的状态码为FIN_WAIT_1

  • 第二次:客户端 <---- 服务端

服务端接收FIN报文,向客户端发送ACK报文,此时客户端状态码为FIN_WAIT_2,服务端告诉客户端:我同意了你的关闭请求

  • 第三次:客户端 <---- 服务端

服务端向客户端发送FIN报文,请求关闭连接。此时服务端状态码:LAST_ACK

  • 第四次:客户端 ----> 服务端

客户端接受FIN报文之后,向服务端发送ACK报文,此时客户端状态码:TIME_WAIT。服务端接收完ACK报文就关闭了连接。客户端在等待2MSL之后如果服务端未回复,客户端就关闭连接。

什么叫MSL? MSL:TCP报文在网络中最长存活时间。

浏览器渲染阶段

当浏览器拿到html文件之后,按照渲染的时间顺序,分为下面几个阶段。

  • 渲染进程将HTML转化成为了DOM树结构。

  • 渲染引擎将CSS样式表转化为浏览器能够读懂的styleSheets,并在这一步计算DOM节点的样式。

  • 构建布局树,计算每个元素的布局信息。

  • 对布局进行分层(图层的意思),生成分层树。

  • 每个图层绘制列表,并将其提交到合成线程,合成线程将图层分图块,将其转化为位图。

  • 渲染完成之后,如果JS操作了DOM节点,会根据操作的幅度面对页面进行重绘或者重排。

  • 合成线程发送绘制图块命令给浏览器进程,浏览器进程根据指令生成页面,并显示到显示器。

构建DOM树

在这一步,HTML解析器会将原始的字节数据转化为文件指定编码的字符,然后根据HTML规范将这些字符转化为标签,最终解析成为了一个树状的对象模型,就是DOM树。具体是通过以下四个步骤,每个步骤都会通过【特定的类】去处理数据

1、转码。Bytes --> Characters【HTMLTokenizer】

2、解析。Characters --> Tokens(标签)【XSSAuditor】

3、构建节点。Tokens --> Nodes【HTMLDocumentParser、HTMLTreeBuilder】

4、创建DOM树。Nodes --> DOM Tree【HTMLConstructionSite】

当DOM树构建完成,Webkit触发DOMContentLoaded事件,当所有资源加载完成会触发onload事件

webkit中html解析器.png

DOM树.png

构建CSSOM树

CSSOM树与DOM树的构建可谓是大同小异。也是通过四个步骤,只是最后一步生成的是CSSOM树。

CSSOM.png

在写代码过程中,有时候会遇到这种问题,控制台说JS报错了,页面直接白屏了,这个问题的原因就在于此:在构建DOM树、CSSOM树的时候,如果有需要执行的JS代码,那就会阻塞DOM树和CSSOM树的构建流程,浏览器会优先执行JS代码,它会把这段代码交给【HTMLScriptRunner】这个类去处理,利用JS引擎来执行JS代码,当执行完毕,继续渲染DOM树和CSSOM树。

这也就是为什么提倡把script标签放到body后面或者给script标签加上defer或者async属性

构建渲染树(Render Tree)

渲染树(Render Tree)是由DOM树和CSSOM树合并而成,但这并不意味着,要等待DOM树和CSSOM树构建完成才开始合并,它们三者没有先后条件,也不是相对独立,而是会有交叉,并行构建,因此会形成一边加载、一边解析、一边渲染的情况。

这一步包括页面布局(排除script、meta等功能性、非视觉标签,排除display:none节点,计算元素信息,确定元素位置,构建一颗包含可见位置的布局树),页面分层(页面上有一些滚动,定位,3D等效果,为了更方便的实现这些效果,浏览器会专门为其生成一颗分层树LayerTree,如图1图2所示。就像一座大楼一样),栅格化(合成线程会按照视口附近的图【优先】生成位图,所谓的栅格化就是将图块转化成完位图 )。

图层.png 图1

图层2.png 图2

栅格化1.png 图3来源

合成线程发送绘制图块命令给浏览器进程。浏览器进程根据指令生成页面,并显示到显示器上,渲染过程完成

参考资料: