Renderer Process 、GPU Process

一、与前端渲染、性能优化相关的,其实主要是Renderer进程和GPU进程。
二、Renderer进程、GPU进程架构
anatomy-of-a-frame.svg
(图片来源:Paul的《The Anatomy of a Frame》)

  • Renderer进程:包括3个线程,合成线程(Compositor Thread)、主线程(Main Thread)、Compositor Tile Worker (Compositor(/[kəm’pɑːzɪtər]/,排字工人) Tile(/taɪl/) Worker)。
  • GPU进程:只有GPU线程,负责接收从Renderer进程中的Compositor Thread传过来的纹理,显示到屏幕上。

    Renderer Process

    一、Renderer进程中3个线程的作用为:

  • Compositor Thread:首先接收vsync信号(vsync信号是指操作系统指示浏览器去绘制新的帧),任何事件都会先到达Compositor线程。如果主线程没有绑定事件,那么Compositor线程将避免进入主线程,并尝试将输入转换为屏幕上的移动。它将更新的图层位置信息作为帧通过GPU线程传递给GPU进行绘制。

    • 当用户在快速滑动过程中,如果主线程没有绑定事件,Compositor线程是可以快速响应并绘制的 ,这是浏览器做的一个优化。
  • Main Thread: 主线程就是我们前端工程师熟知的线程,这里会执行解析html、样式计算、布局、绘制、合成等动作。所以关于性能的问题,都发生在了这里。所以应该重点关注这里 。
  • Compositor Tile Worker:由合成线程产生一个或多个worker来处理光栅化的工作。

二、Service Workers和Web Workers可以暂时理解也在Renderer进程中,这里不展开讨论。

Main Thread / 主线程

main-thread.svg
(图片来源:Paul的《The Anatomy of a Frame》)
一、主线程是我们的代码真实存在的环境。
二、有个红色的箭头,从Recal Styles和Layout指向了requestAnimationFrame,这意味着有Forced Synchronous Layout (or Styles)(强制回流和重绘)发生,这一点在性能方面特别要注意。
三、在Main Thread中,有这几个需要注意一下:

  • requestAnimationFrame:因为布局和样式计算是在rAF之后,所以在rAF是进行元素变更的理想时机。如果在这里对一个元素变更100个类,不会进行100次计算,它们会分批以后处理。需要注意的是,不能在rAF中查询任何计算样式和布局的属性(例如:el.style.backgroundImage或el.style.offsetWidth),因为这样会导致重绘和回流。
  • Layout:布局的计算通常是针对整个文档的,并且与DOM元素的大小成正比!(这点特别要注意,如果一个页面DOM元素太多,也会导致性能问题)

四、主线程的顺序始终都是:Input Event Handler->requestAnimationFrame->ParseHtml->ReculateStyles->Layout->Update Layer Tree->Paint->Composite->commit->requestIdleCallback,只能从前往后,

  • 例如,必须先是ReculateStyles,然后Layout、然后Paint。
  • 但是,如果它只需要做最后一步Paint,那么这就是它全部要做的事情,不会再发生前面的ReculateStyles和Layout。
    • 这里其实给了我们一个启示: 如果要让fps保持60,即每帧的js执行时间少于16.66ms,那么让这个主线程执行的过程尽可能地少,是我们的性能优化目标 。
  • 根据主线程的这些步骤,理想的情况下,我们只希望浏览器只发生最后一个步骤:Composite(合成)。

css

五、CSS的属性是我们需要关注一下的模块。

【示例】csstrigger描述了哪些CSS属性会引起重绘、回流和合成。
1、让我们给一个元素进行移动位置时: transform 和 opacity 可以直接触发合成,但是 left 和 top 却会触发Layout、Paint、Composite3个动作。所以显然用transform是更好的方案。
2、但这并不是说我们不应该用left和top这些可能引起重绘回流的属性,而是应该关注每个属性在浏览器性能中引起的效果 。