0%

Renderer进程

Renderer进程包含什么

  • 主线程
  • worker线程(包括web worker和service worker)
  • compositor线程
  • raster线程

核心工作是将 HTML、 CSS 和 JavaScript 转换成用户可以与之交互的网页。

解析

构造DOM

DOM是浏览器对页面的内部表示,开发者具有可与之交互的API。根据HTML标准,HTML具有很好的容错性。

子资源加载

解析构建DOM时,有个preload scanner,它可以并发运行获取一些外部资源,如图片、CSS和JS。在浏览器进程中向网络线程发送请求。

JS阻止解析

HTML一旦遇到script标签,就停止解析HTML。继而加载解析和执行JS代码。因为可以通过document.write这样的API修改DOM结构。有一篇很好的关于V8如何执行JS的文章

提示浏览器如何加载资源

如果JS不使用document.write,可以标记scriptasync或者defer,浏览器就可以异步加载运行JS代码,不阻止解析。也可以使用javascript module<link rel="preloa">是一种告诉浏览器当前导航需要该资源的方式,希望尽快下载。

计算样式

使用CSS可以对页面进行样式化,主线程解析CSS确定每个DOM结点的计算样式。浏览器默认存在一个样式表。

布局

主线程遍历DOM和计算样式,构建布局树,其中包含元素坐标以及边界框大小。并且只包含课件内容。visibility:hidden位于布局树中,display:none不位于布局树中。伪元素::before会包含在布局树,而不在DOM树。

chrome有一个工程师团队负责布局,这是一些工作细节

绘制

该阶段负责判断绘制的顺序。如果设置了z-index则不能单纯按HTML元素顺序绘制。

主线程遍历布局树,创建paint records,是绘制过程的一个描述,例如“先画background,然后text,然后rectangle”。类似于JS绘制canvas。

更新渲染pipeline很昂贵

布局树发生变化,绘制顺序也会发生变化。前面的操作会影响后面的操作。在给元素配置动画,浏览器需要在每个帧之间操作动画,如果每一帧都移动物体,动画显得很流畅。但是如果错过中间帧,页面就会janky

应用程序运行JS时,动画也可能会被阻塞,因为他们运行在主线程上。从而显得janky

可以将JS操作分块。使用requestAnimationFrame在每一帧运行。也可以在web worker运行JS

合成

浏览器在这个过程开始前已经知道了文档结构,元素样式,页面的几何特征和绘制顺序。将这些信息转换为屏幕上的像素,叫做rasterizing(光栅化)。chrome将页面各部分分层,分别光栅化,然后组合成一个框架,当滚动页面,这些已经光栅化的层组合成一个新的框架。

分层

为了找出元素需要在哪里。主线程遍历布局树创建层树(layer tree),在开发者工具的performance面板中叫做update layer tree。如果有些部分应该是单独的层而没有在单独的层中,可以使用CSS的will-change属性提示浏览器。

raster和composite脱离主线程

创建层树,确定绘制顺序后,主线程将信息交给compositor线程。compositor线程光栅化每一层。一个层可以像页面那么大,所以compositor将其分成几个tiles,每一个tile因此发送给raster线程,raster线程光栅化每一个tile,存储在GPU内存。

compositor线程可以优先处理不同的raster线程,所以视口附近的东西可以优先光栅化。一个layer也有许多不同分辨率的tillings用于处理zoom-in这样的行为。

一旦tiles光栅化完毕,compositor线程就会收集draw quads来创建一个compositor frame。

draw quads包含一些信息,例如tile在内存中的位置,以及在考虑到页面合成的情况下要绘制tile的页面位置

compositor frame是一个表示页面frame的draw quads集合。

然后通过 IPC 将compositor frame提交到浏览器进程。此时,可以从 UI 线程为浏览器 UI变化添加另一个compositor frame,或者从其他renderer进程为extension添加另一个compositor frame。这些compositor frame被发送到 GPU 在屏幕上显示。如果出现滚动事件,compositor线程将创建另一个compositor frame并发送到 GPU。

compositing的好处是它不需要涉及主线程。compositor线程不需要等待样式计算或 JavaScript 执行。这就是为什么compositing动画被认为是最好的平稳性能。如果布局或者绘制需要再次计算,那么主线程必须参与。