简单渲染前过程描述

  1. 用户输入域名,DNS解析域名为IP地址
  2. 浏览器根据IP地址请求服务器
  3. 服务器响应HTTP请求,并返回数据给浏览器
  4. 浏览器开始渲染

具体细节:

  1. 当用户输入域名URL后,浏览器进程(一般一个标签页对应一个单独的进程,但是一些空白页会被合并为一个进程)中的UI线程会建立一个网络线程网络线程会根据DNS对域名进行解析获取IP(如果用户输入的是一些关键字/本地文件路径,就会到对应的搜索引擎/本地文件)。得到IP后,浏览器的SafeBrowsing会对IP进行安全检查(是否在黑名单),如果判定为不安全会提供一个禁止访问的页面,如果安全将与服务器进行通信(TCP三次握手,返回响应报文)通知UI线程进行下一步工作。

  2. UI线程此时将新建一个渲染进程,并且通过IPC管道传输HTML文件给渲染进程主线程,此时,正式开始渲染。

🏊‍♂️主线程

  1. 主线程中,会将HTML文件进行词法分析转义为Token,即Tokenization(标记化)

  2. 并且创建DOM节点,最后创建成DOM

    上述 1,2 被称为HTML解析

  3. 但解析过程中,可能遇到<link>与<style>,就会涉及到CSS的解析(为了提升解析效率,浏览器在解析前会启动一个预解析,会优先下载cssjs的资源文件。当CSS文件还未下载的时候,主线程并不会阻塞,这是因为CSS的下载与解析是在预解析线程中执行的,CSS解析完成后将生成CSSOM树。

    img

  4. 解析过程中,可能遇到<script>等标签,这就涉及到了JS的解析,而JS的解析会阻塞当前的HTML解析,在JS解释完毕后再继续,这是因为我们不清楚JS是否会操作DOM节点或者操作CSS节点。

    因此,我们需要注意JS的引入时间,一般JS的加载位置应该放在BODY标签的底部,或者使用Defer

  5. 此时,我们已经得到了解析好的DOM树与CSSOM树,此时进行Computed Style,得到一棵带有样式DOM

  6. 但是还不够,我们还需要得到Layout来获取每个节点的几何信息,通过这个计算过程后,我们将得到一棵带有页面x,y坐标以及盒子尺寸Layout Tree,此时Layout Tree与我们真实能看到的内容是一一对应的了。

    Layout TreeDOM并非一一对应,其中不可视的display: none不会在Layout Tree上,而伪类元素(::before等)会挂载在Layout Tree的相应节点

  7. 但是仍然不够,此时还需要注意,如果两个节点的位置大小有重合的话,我们无法确定哪个节点展示在前,哪个被覆盖,因此我们还需要确认各个节点的绘制顺序,即分层

  8. 分层工作完成后,将生成绘制指令,将每个层单独生成绘制指令。==只是生成,并未执行==

🏊‍♀️合成线程

  1. 合成线程会先对图层进行分块处理得到Tiles(会从线程池获取栅格线程的帮助)

  2. 分块完成后,将进行光栅化阶段,这一过程会交付给GPU进程,最终得到位图。

  3. 最后,当所有的图块都被栅格化后,合成线程将生成一个个指引quad信息,并通过IPC给浏览器进程发送一个渲染帧,这个渲染帧最终将交由GPU进行显示,每当页面滚动的时候,合成线程都会交付另一个渲染帧给GPU来更新页面

    指引会标识位图的位置,以及考虑旋转,缩放平移等信息,与渲染主线程无关。这就是为什么transform效率高的本质原因,以及不会引起回流(重排)与重绘阻塞渲染。

相关问题

1. Reflow 回流/重排

​ 当进行影响Layout Tree的操作的时候,需要重新计算Layout Tree,而且为了避免连续的多次布局反复计算,浏览器会合并这些操作,当JS代码全部完成后再统一计算,改动属性造成的Reflow是异步完成的。也正因如此,JS获取布局属性的时候可能会无法获取到最新的布局信息。

2. Repaint 重绘

​ 重绘,重新绘制渲染树,一般不影响DOM树,不一定引起回流,而回流一定引起重绘。