面试官:你知道输入一个url到页面渲染发生了什么吗?

170 阅读10分钟

我正在参加「掘金·启航计划」点击活动查看详情

这道题你是不是经常在面试中被问到呢?所以从url到页面渲染,到底发生了什么呢?请不要放过每一个点。先来普及一些小知识点。

文本对象模型

DOM 构建是增量的。 HTML 响应变成令牌(token),令牌变成节点,而节点又变成 DOM 树。 单个 DOM 节点以 startTag 令牌开始,以 endTag 令牌结束。 节点包含有关 HTML 元素的所有相关信息。 该信息是使用令牌描述的。 节点根据令牌层次结构连接到 DOM 树中。 如果另一组 startTag 和 endTag 令牌位于一组 startTag 和 endTag 之间,则您在节点内有一个节点,这就是我们定义 DOM 树层次结构的方式。在下面会有例图展示,继续往下翻哦。

节点数量越多,关键渲染路径中的后续事件将花费的时间就越长。

渲染树

渲染树包括了内容和样式:DOM 和 CSSOM 树结合为渲染树。为了构造渲染树,浏览器检查每个节点,从 DOM 树的根节点开始,并且决定哪些 CSS 规则被添加。

渲染树只包含了可见内容。头部(通常)不包含任何可见信息,因此不会被包含在渲染树种。如果有元素上有 display: none;,它本身和其后代都不会出现在渲染树中。

从url到页面渲染

url解析 → DNS查询 → TCP连接 → 处理请求 → 接收响应 → 渲染页面

  • url解析

  1. 用户输入

如果是搜索内容,地址栏会使用浏览器默认的搜索引擎,来合成新的带搜索关键字的 URL。如果判断输入内容符合 URL 规则,比如输入的是 baidu.com,那么地址栏会根据规则,把这段内容加上协议,合成为完整的 URL,

  1. URL请求过程

接下来,便进入了页面资源请求过程。这时,浏览器进程会通过进程间通信(IPC Inter-Process Communication)把 URL 请求发送至网络进程,网络进程接收到 URL 请求后,会在这里发起真正的 URL 请求流程

首先,网络进程会查找本地缓存是否缓存了该资源。如果有缓存资源,那么直接返回资源给浏览器进程;如果在缓存中没有查找到资源,那么直接进入网络请求流程。这请求前的第一步是要进行 DNS 解析,以获取请求域名的服务器 IP 地址。如果请求协议是 HTTPS,那么还需要建立 TLS 连接。

接下来就是利用 IP 地址和服务器建立 TCP 连接。连接建立之后,浏览器端会构建请求行、请求头等信息,并把和该域名相关的 Cookie 等数据附加到请求头中,然后向服务器发送构建的请求信息。

服务器接收到请求信息后,会根据请求信息生成响应数据(包括响应行、响应头和响应体等信息),并发给网络进程。等网络进程接收了响应行和响应头之后,就开始解析响应头的内容了。

DNS解析

DNS也就是域名系统,是用于实现域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被及其直接读取的IP数串。通过主机名,得到该主机名对应的IP地址的过程叫域名解析。

常见的 DNS服务器就是两种:权威解析器和递归解析器。递归解析器也可以叫做localDNS。

这个过程大概能分十步:

①本地电脑先检查浏览器缓存 ;

②本地电脑检查host配置文件 ;

③用户向本地DNS服务器发起DNS解析请求 ;

④如果本地DNS服务器仍然没命中,就直接到根DNS服务器请求解析;

⑤根DNS服务器返回给本地DNS一个顶级DNS服务器地址;

⑥ 本地DNS服务器再向上一步获得的顶级DNS服务器发送解析请求;

⑦ 接受请求的顶级DNS服务器查找并返回此域名对应的Name Server域名服务器的地址,这个Name Server服务器就是要访问的网站域名提供商的服务器,其实该域名的解析任务就是由域名提供商的服务器来完成。 比如我要访问www.baidu.com,而这个域名是从A公司注册获得的,那么A公司上的服务器就会有www.baidu.com的相关信息;

⑧ Name Server服务器会查询存储的域名和IP的映射关系表,再把查询出来的域名和IP地址等等信息,连同一个TTL值返回给本地DNS服务器;

⑨ 返回该域名对应的IP和TTL值,本地DNS服务器会缓存这个域名和IP的对应关系,缓存时间由TTL值控制;

⑩把解析的结果返回给本地电脑。

本地电脑根据TTL值缓存在本地系统缓存中,域名解析过程结束在实际的DNS解析过程中,可能还不止这10步,如Name Server可能有很多级,或者有一个GTM来负载均衡控制,这都有可能会影响域名解析过程。

最终的域名解析其实就是域名提供服务商服务器要做的工作,NameServer就是一个映射表,相当于你的微信号所对应的你的绑定的电话号码(域名&IP)

xfRAeJ.png

接中的三次握手

  • 第一次握手:客户端向服务端发送携带有看客户端数据通讯初始报文序列号的报文,并进入SYN-SENT状态,当客户端收到数据时证明服务端知道:客户端的发送能力、服务端的接收能力正常。
  • 第二次握手:服务端将响应客户端数据通讯初始序列号和服务端数据通讯初始序列号放入报文中一起发送给客户端。发送结束后服务端进入SYN-RECEIVED状态。当客户端接受到数据证明在客户端的角度上知道:客户端和服务端的发送和接收能力都是正常的。
  • 第三次握手:客户端向服务端 发送一个确认应答,并进入ESTABLISHED状态,服务端收到应答后也进入ESTABLISHED状态完成三次握手。这时候在服务端的角度知道:服务端的发送能力、客户端的接收能力正常。

总结:通过三次挥手服务端和客户端才都能知道它们双方的发送能力和接收能力都是正常的,这样才能建立连接。如果没有第三次握手会怎样呢?服务端并不知道自己的发送能力和客户端的 接收能力是否正常,除此之外,还可以防止数据报延迟带来的 不必要的资源消耗:

当客户端发起TCP第一次握手的请求时,因为网络问题滞留在网络中,这时候客户端因为等待太久没有响应,所以自动重发了握手请求并完成了所有数据请求,关闭了与服务端的连接,这时候服务端才接收到第一次滞留在网络中的握手请求,服务端会向客户端发送第二次握手并处于等待第三次握手的状态,客户端接收到第二次握手请求后,发现是过期的请求就可以通过停止发送第三次握手,服务端一段时间没接到第三次握手请求会自动关闭请求监听。

渲染页面

DOM树 -> CSSOM树 -> 渲染树 -> GPU绘制

在渲染引擎内部,有一个叫HTML解析器的模块,它的职责就是负责将HTML字节转换为DON结构。

DOM树的生成过程:字节流 (bytes) -> 分词器(token) -> 生成节点(node)-> DOM

解析HTML的过程:Tag Token又分StartTag 和 EndTag,HTML解析器维护了一个Token栈结构,这个栈结构在后面是如何生成node节点的,遵循以下规则:

  1. 如果压入到栈中的是Start Token,HTML解析器会为该Token创建一个DOM节点,然后将该节点加入到DOM中,它的父节点就是栈中相邻的那个元素生成的节点。
  2. 如果分词器解析出来的是文本Token,那么就会生成一个文本节点,然后将该节点加入到DOM树中,文本Token是不需要压入到栈中,它的父节点就是当前栈顶Token所对应的DOM节点。
  3. 如果分词器解析出来的是EndTag 标签,比如EndTag div,HTML解析器会查看Token栈顶是否是StarTag div,如果是,就将StarTag div从栈中弹出,表示该div元素解析完成。

下面来段例子做出解释。

<html>
<body>
    <div>呆呆鹅</div>
</body>
</html>

xfRaSf.png

xfRDmQ.png

xfRI0J.png

  1. DOM树生成:HTML解析器在开始工作时,会默认创建一个根为document的空 DOM 结构,同时会将一个StartTag document 的Token 压入栈底。然后分词器解析出来的第一个StartTag html的Token会被压入到栈中,并且创建第一个html的DOM节点,添加到document上,当遇到StartTag div 时 则会生成一个div的节点,而文本呆呆鹅则会直接加入到DOM树中,并不会进入到Token栈里,此后分词器解析出第一个EndTag div ,这个时候HTML解析器会去判断当前栈顶是否是StartTag div,如果是StartTag div就会从栈顶弹出StartTag div。接下来的解析都是按照上面的那三个规则。

  2. CSSOM树:过程类似于 DOM 的产生,但是这个过程更加消耗性能,因为CSS是可以自己定义的,也可以继承得到。这个过程浏览器需要递归得到CSSOM树,这样才能确定每一个元素到底是什么样式。

  3. 渲染树:DOM + CSSOM生成Render Tree(渲染树)(display: none节点不会出现在渲染树中)渲染树长什么样呢,其实就是在DOM树的基础上给每个节点注明了属性,这里就不展示了,画图不易,小编好累。

  4. GPU绘制

四次挥手

客户端接收到服务端发送响应请求后便需要通过4次握手关闭连接,释放服务器资源

  1. 客户端发送申请关闭连接请求,此时客户端进入FIN-WAIT-1装态。
  2. 服务端收到关闭链接请求后便向客户端发送ACK确认包,并进入CLOSE-WAIT状态,客户端接收到ACK确认包后进入 FIN-WAIT-2状态。
  3. 服务端发送完所有未发送的数据后便向客户端发送FIN数据包,并进入LAST-ACK状态。
  4. 客户端收到服务端发送的FIN数据包后便向服务端发送确认包并进入TIME-WAIT状态,并自动在2MSL时钟后关闭链接监听,服务端再接收到确认包后直接关闭监听。

挥手完毕,初次渲染完成,所以现在对重排和重绘是不是也理解更深了呢?