关于前端:从-Chrome-看浏览器的渲染机制

43次阅读

共计 2927 个字符,预计需要花费 8 分钟才能阅读完成。

Chrome 的多过程架构

过程 & 线程

在谈浏览器多过程架构之前,咱们先聊聊过程和线程的概念。

过程是零碎进行资源调度和调配的的根本单位,一个过程能够认为是一个程序的运行实例。启动一个程序的时候,操作系统会为该程序创立一块内存,用来寄存代码、运行中的数据和一个执行工作的主线程,咱们把这样的一个运行环境叫过程。

线程是依附于过程的,是操作系统可辨认的最小执行和调度单位,而过程中应用多线程并行处理能晋升运算效率,多线程能够并行处理工作,然而线程是不能独自存在的,它是由过程来启动和治理的。咱们最相熟的 JS 的运行就是线程这个维度。

上面是过程和线程间的一些区别:

  • 一个线程依附于一个过程,一个过程能够有多个线程;
  • 线程之间共享过程中的数据。
  • 过程中的任意一线程执行出错,都会导致整个过程的解体,而过程之前不会相互影响。
  • 当一个过程敞开之后,操作系统会回收过程所占用的内存。
  • 过程之间的内容互相隔离,只能通过 IPC 通信。

我感觉知乎上一个用火车比喻过程和线程的例子比拟形象:过程好比火车,线程好比车厢,一辆火车有多节车厢,不同的火车之间互不烦扰,然而一节车厢失火会殃及多节车厢。

Chrome 架构

Chrome 浏览器包含:1 个 Browser 主过程、1 个 GPU 过程、1 个 Utility 过程、多个 Renderer 过程和多个 Plugin 过程。

  • Browser 主过程(浏览器过程):次要负责界面显示、用户交互、子过程治理,同时还蕴含网络申请和文件拜访等。
  • GPU 过程:与其余过程隔离解决 GPU 工作。
  • Renderer 过程(渲染过程):外围工作是将 HTML、CSS 和 JavaScript 转换为用户能够与之交互的网页,排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该过程中,默认状况下,Chrome 会为每个 Tab 标签创立一个渲染过程。出于平安思考,渲染过程都是运行在沙箱模式下。
  • Plugin 过程:次要是负责插件的运行,因插件易解体,所以须要通过插件过程来隔离,以保障插件过程解体不会对浏览器和页面造成影响。

浏览器渲染机制

渲染过程的外围工作是将 HTML、CSS 和 JavaScript 转换为用户能够与之交互的网页。在这个工作过程中,输出的 HTML 通过一些子阶段,最初输入像素。依照渲染的工夫程序,这些子阶段大抵可分为:构建 DOM 树、计算款式、布局、分层、绘制、分块、栅格化和合成。

构建 DOM 树

当渲染过程开始接管 HTML 数据时,主线程开始解析 HTML 并将其转换为浏览器可能了解的 DOM 树结构。

在 DOM 树的解析过程中,如果遇到 img、css 或者 js 资源时,主线程会向 Browser 主过程的网络线程发送申请以获取对应的资源。

当解析的过程中遇到 <script> 标签时,主线程会暂停 HTML 的解析,从而进行 js 代码的加载、解析和执行。因为 js 代码中可能波及对页面构造的批改,主线程必须期待 js 运行能力复原对 HTML 文档的解析。因而咱们能够通过在 <script> 标签上加上 async 或者 defer 属性来异步加载执行 js 代码,防止 js 阻塞 HTML 的解析。

款式计算

款式计算的目标是为了计算出 DOM 节点中每个元素的具体款式,在计算过程中须要恪守 CSS 的继承和层叠两个规定,这个阶段大体可分为三步来实现:

  • 把 CSS 转换为浏览器可能了解的构造:当渲染引擎接管到 CSS 文本时,会执行一个转换操作,将 CSS 文本转换为浏览器能够了解的构造——StyleSheets;
  • 转换样式表中的属性值,使其标准化:例如 rem 这些属性,须要将所有值转换为渲染引擎容易了解的、标准化的计算值;
  • 计算出 DOM 树中每个节点的具体款式


在浏览器中,咱们能够通过 Computed 面板查看以后节点的 Computed Style。

布局

有了 DOM 树和 DOM 对应的 Computed Style 之后还不足以显示页面,接下来还须要计算出 DOM 树中可见元素的几何地位,这个计算过程叫做布局。

Chrome 在布局阶段须要实现两个工作:创立布局树和布局计算。

  • 创立布局树:浏览器会遍历 DOM 树中的所有可见节点,并把这些节点加到布局树中,而不可见的节点会被布局树疏忽掉,如图中 span 这个元素被设置为 dispaly:none,这个元素会被布局树疏忽;
  • 布局计算:有了布局树后,浏览器会计算布局节点的坐标地位;

分层

有了布局树后,对于一些简略页面曾经具备绘制条件了,然而对于咱们古代的页面来说,有很多简单的成果,如一些简单的 3D 转换、z-index 做 z 轴排序等,对于这些场景为了页面展现的正确性,渲染引擎还会为特定的节点生成专用的图层,并生成一棵对应的图层树。

须要留神的是,并不是每个节点都蕴含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点所在的图层。最终每一个节点都会间接或者间接地从属于一个图层。

通常满足上面两点便会晋升为一个独自的图层:

  • 领有层叠上下文属性的元素会被晋升为独自的一层。层叠上下文
  • 须要剪裁的中央也会被创立为图层(overflow)。

咱们能够在浏览器的 layers 面板看到以后页面的分层状况:

绘制

在确定了 DOM 树、计算款式以及布局树依然不足以绘制页面,这里还须要有明确的绘制程序,在此过程中主线程会遍历布局树并创立绘制记录。

同样,咱们能够在浏览器的 layers 面板看到当前页面对应图层的绘制记录:

光栅化(栅格化)

在确定了布局树并创立了图层以及对应的绘制程序之后,主线程会将信息提交给合成线程。合成线程会去光栅化每一个图层。

因为咱们浏览器的视口是无限的,然而页面的长度可能很长,有些图层可能超过视口很多,而用户对页面的感知是视口维度的,一次性渲染整个图层未免有些节约,因而合成线程会对图层进行 分块解决

有了图块之后,合成线程会将每一个图块发送到光栅线程(Raster thread),光栅线程会光栅化每一个图块并存在 GPU 内存中。在这个过程中,合成线程会优先选择视口内的图块提交给光栅线程。

合成和展现

光栅化实现后,合成线程会创立合成帧通过 IPC 通信提交给浏览器过程。浏览器过程接管到指令后会将内容绘制在内存中并展现在屏幕上。

至此,从接管 HTML 数据到页面的展现全流程就完结了。上面咱们再联合浏览器的渲染流程看下什么是重排、重绘和合成。

重排、重绘和间接合成

重排

当咱们通过 js 或者 css 属性更新了元素的几何属性,例如元素的宽度、高度,此时浏览器会从新触发布局并从新执行前面全副的渲染流程,因而,重排的开销是最大的。

重绘

当咱们通过 js 或者 css 更新元素的绘制属性,例如元素的背景色、文字的色彩等,此时布局和分层阶段被省略,只执行后续的流程,因而重绘的开销相比重排会小很多。

间接合成

为什么咱们为了防止重排和重绘而去采纳 css3 的 transform 等属性呢?因为此时整个主线程的流程会被全副跳过,执行后续的流程,而后续的流程交给了在执行线程、光栅线程和 GPU 过程上执行没有占据主线程的资源,因而效率是最高的。

参考文章

  • https://developer.chrome.com/…
  • https://developer.chrome.com/…
  • https://developer.mozilla.org…
  • https://www.zhihu.com/questio…
正文完
 0