likes
comments
collection
share

面试官:从输入 URL 到页面加载完成发生了什么?

作者站长头像
站长
· 阅读数 4

这是一个很经典的面试问题,根据面试者的知识水平和掌握情况,所能表达的内容广度和深度也是截然不同的。

从DNS、TCP、HTTP到浏览器运行逻辑,以及之后的渲染流程等等,它能包含的内容非常多。面试官完全可以从这里开始,然后进而逐步拓展到其他问题,如:网络相关、浏览器相关、JS与CSS相关、优化相关等等。

所以,大致了解下这个流程还是很有必要的。希望下次面试的时候,面试官问及这个问题时,你可以自信的说出这句话——你想让我说多久吧🤓?

本文为面试专题之JavaScript进阶——原理篇系列。

需要提前了解的知识点

首先这个过程是需要各个进程之间相互配合的,涉及:

  • 浏览器进程:主要负责用户交互、子进程管理和文件储存等功能。
  • 网络进程:是面向渲染进程和浏览器进程等提供网络下载功能。
  • 渲染进程:的主要职责是把从网络下载的 HTMLJavaScriptCSS图片等资源解析为可以显示和交互的页面。

用户从地址栏输入

当用户在地址栏中输入一个查询关键字时,地址栏会判断输入的关键字是否为搜索内容

  1. 如果是搜索内容,地址栏会使用浏览器默认的搜索引擎,来合成新的带搜索关键字的 URL
  2. 如果判断内容符合 URL 规则,那么地址栏会根据规则,把这段内容加上协议,合成为完整的 URL
  • 是web网络协议的情况 https://www.baidu.com
  • 不是网络协议的情况:file:///C:/Users/sunss/Desktop/ES6.pdf

URL 请求过程

接下来,便进入了页面资源请求过程。

这时,浏览器进程会通过进程间通信(IPC)URL 请求发送至网络进程,网络进程接收到 URL 请求后,会在这里发起真正的 URL 请求流程。

  • 有缓存:网络进程首先查找本地是否有缓存,如果有,则直接返回缓存数据,不再发起真正的网络请求。
  • 无缓存:如果在缓存中没有查找到资源,那么直接进入网络请求流程

网络请求流程:

  1. DNS 解析,获取请求域名的服务器 IP 地址
  2. 利用 IP 地址和服务器建立 TCP 连接(如果请求协议是 HTTPS,那么还需要建立 TLS 连接)
  3. 连接建立之后,浏览器端会构建请求头等信息,并把和该域名相关的 Cookie 等数据附加到请求头中,然后向服务器发送构建的请求信息。
  4. 服务器接收到请求信息后,会根据请求信息生成响应数据(响应头和响应体等信息),并发给网络进程。
  5. 等网络进程接收了响应行和响应头之后,开始解析响应头的内容

状态码

根据返回的状态码信息,会做出对应的处理,目前主要是这5种分类:

  • 1xx开头的:该类状态码属于提示信息,是协议处理中的一种中间状态,实际用到的比较少。
  • 2xx开头的:该类状态码表示服务器成功处理了客户端的请求,也是我们最愿意看到的状态。
  • 3xx开头的:该类状态码表示客户端请求的资源发送了变动,需要客户端用新的 URL 重新发送请求获取资源,也就是重定向。
  • 4xx开头的:该类状态码表示客户端发送的报文有误,服务器无法处理,也就是错误码的含义。
  • 5xx开头的:该类状态码表示客户端请求报文正确,但是服务器处理时内部发生了错误,属于服务器端的错误码。

本文篇幅有限,这里我们只着重介绍 3xx 类型,这里只需重点了解:重定向(即 301、302)

3xx

  • 301 Moved Permanently:表示永久重定向,说明请求的资源已经不存在了,需改用新的 URL 再次访问。
  • 302 Found:表示临时重定向,说明请求的资源还在,但暂时需要用另一个 URL 来访问。
  • 304 Not Modified:不具有跳转的含义,表示资源未修改,重定向已存在的缓冲文件,也称缓存重定向,用于缓存控制。

301 和 302 都会在响应头里使用字段 Location ,指明后续要跳转的 URL,浏览器会自动重定向新的URL。

重定向之 301与302

重定向的两个基本条件:

  1. 响应状态码为 3xx
  2. 响应头中必须包含 Location字段(该字段用于表示要重定向到的位置)

基本概念:

  • 301是永久重定向
  • 302是临时重定向

应用场景:

  1. http网站跳转到https网站
  2. 二级域名跳转到主域名,http://www.abc.com跳转到http://abc.com
  3. 404页面失效跳转到新的页面
  4. 老的域名跳转到新的域名

一些说明:

  • 浏览器会缓存“301”永久重定向的页面,而不会缓存“302”临时重定向的页面。

响应数据类型处理

处理完重定向等跳转信息之后,浏览器会根据响应数据类型,进行不同的处理,如:下载、HTML页面

  • Content-Type:它是 HTTP 头中一个非常重要的字段, 它告诉浏览器服务器返回的响应体数据是什么类型
    • HTML 文档,如 text/html
    • 文件下载,如 application/octet-stream
  1. 如果是下载类型,那么该请求会被提交给浏览器的下载管理器,同时该 URL 请求的导航流程就此结束。
  2. 如果是HTML类型,那么接下来开始准备渲染进程。

渲染进程准备阶段

渲染进程准备好之后,还不能立即进入文档解析状态,因为此时的文档数据还在网络进程中,并没有提交给渲染进程,所以这一步就进入了提交文档阶段。

默认情况下,Chrome 会为每个页面分配一个渲染进程(某些场景下会出现几个Tab合并在一个进程中的情况)

process-per-site-instance策略:如果从一个页面打开了另一个新页面,而新页面和当前页面属于同一站点的话,那么新页面会复用父页面的渲染进程

浏览器进程通知渲染进程和网络进程建立传输数据的“管道”,传输完成之后,浏览器进程会更新浏览器界面状态(包括了安全状态、地址栏的 URL、前进后退的历史状态)

渲染阶段

浏览器渲染过程如下

  • 解析HTML,生成DOM树
  • 解析CSS,生成CSSOM树
  • 将DOM树和CSSOM树结合,生成渲染树(Render Tree)
  • Layout(回流):根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小)
  • Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素
  • Display:将像素发送给GPU,展示在页面上。(这一步其实还有很多内容,比如会在GPU将多个合成层合并为同一个层,并展示在页面中。而css3硬件加速的原理则是新建合成层)

渲染过程详细分析

  1. 渲染进程将 HTML 内容转换为能够读懂的DOM 树结构。
  2. 渲染引擎将 CSS 样式表转化为浏览器可以理解的styleSheets,计算出 DOM 节点的样式。
  3. 创建布局树,并计算元素的布局信息。
  4. 对布局树进行分层,并生成分层树。
  5. 为每个图层生成绘制列表,并将其提交到合成线程。
  6. 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。
  7. 合成线程发送绘制图块命令(DrawQuad)给浏览器进程。
  8. 浏览器进程根据图块命令(DrawQuad)消息生成页面,并显示到显示器上。

基本概念解释

分层

因为页面中有很多复杂的效果,如一些复杂的 3D 变换、页面滚动,或者使用 z-indexingz 轴排序等,为了更加方便地实现这些效果,渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree)。

符合条件会创建新的层:

  • 拥有层叠上下文属性的元素会被提升为单独的一层
  • 需要剪裁(clip)的地方也会被创建为图层

绘制

渲染引擎会把一个图层的绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表

绘制操作是由渲染引擎中的合成线程来完成的(由主线程绘制列表提交给合成线程

绘制列表 => 合成线程

合成线程的工作做过程:

前置原因分析:有些情况下,有的图层可以很大,有的页面你使用滚动条要滚动好久才能滚动到底部,但是通过视口,用户只能看到页面的很小一部分,所以在这种情况下,要绘制出所有图层内容的话,就会产生太大的开销,而且也没有必要。(图块大小一般为:256×256 / 512×512)

视口:屏幕上页面的可见区域

  1. 合成线程会将图层划分为图块(tile)
  2. 按照视口附近的图块来优先生成位图(生成位图的操作是由栅格化来执行的。所谓栅格化,是指将图块转换为位图)
    • 图块是栅格化执行的最小单位
    • 渲染进程维护了一个栅格化的线程池,所有的图块栅格化都是在线程池内执行的
    • 通常,栅格化过程都会使用 GPU 来加速生成
  3. 合成和显示:一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令——“DrawQuad”,然后将该命令提交给浏览器进程。

浏览器进程里面有一个叫 viz 的组件,用来接收合成线程发过来的 DrawQuad 命令,然后根据 DrawQuad 命令,将其页面内容绘制到内存中,最后再将内存显示在屏幕上。

重排、重绘、合成

回流一定会触发重绘

  • 重排:更新了元素的几何属性,如:
  • 重绘:更新元素的绘制属性,如:背景颜色
  • 合成:更改一个既不要布局也不要绘制的属性,如:transform

对比:

  • 重排需要更新完整的渲染流水线,所以开销也是最大的。
  • 相较于重排操作,重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些。
  • 相对于重绘和重排,合成能大大提升绘制效率。

因此,如果要做性能优化,首先要考虑避免多次重排

如何避免触发回流和重绘

CSS

  • 避免使用table布局。
  • 尽可能在DOM树的最末端改变class
  • 避免设置多层内联样式。
  • 将动画效果应用到position属性为absolutefixed的元素上
  • 避免使用CSS表达式(例如:calc()
  • CSS3硬件加速(GPU加速)

JavaScript

  • 避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性
  • 避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中
  • 也可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘
  • 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来
  • 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流

总结

本篇的内容其实有点类似于一本书的目录,

从目录看,它不要求你对每一章都非常深入,但是对于每一步主要是干了什么,你应该知道。

不过,就自己而言,你可以根据这个目录去逐步掌握学习每一章的内容,直到把这本书看完~

当面试官随手翻开一个目录,问你这章主要干了什么时,也希望你可以流畅对答。

交流

好了,本文到此结束,欢迎来撩,一起学习🙋‍♂️~

想要了解面试专题进阶篇的,请持续关注面试专栏的更新~

面试相关的文章及代码demo,后续打算在这个仓库(JS-banana/interview: 面试不完全指北 (github.com))进行维护,欢迎✨star,提建议,一起进步~

资料