从一道面试题探究渲染问题

354 阅读3分钟

问题:

问题1:以下代码会发生什么

function demo() {
  var d = Date.now()
  document.body.style.backgroundColor = 'blue'
  while (Date.now() - d < 2000) {}
  document.body.style.backgroundColor = 'red'
}

答:卡死2秒。然后 body 背景变红

接着追问问题2:如果此时有个css动画,会发生什么?

答:css 动画卡死2秒。面试官回答:动画不变。

思考

问题1:很简单,因为JS引擎和GUI引擎用的是同一个线程,当你JS引擎无限Scripting,是无法走到GUI渲染这一步。所以卡死2秒。

问题2:面试官的回答是动画不变。个人是不相信的。只要触发重排,重绘,肯定要使用到GUI引擎去重新render的。在实验中也发现,我们2个只是各对了一半 。

一般只有重排和重绘会GUI渲染,所以我的理解中,当JS引擎在执行超时的计算,此时页面是没法触发重排和重绘。

什么是重排重绘

重排:当我们对 DOM 的修改引发了 DOM 几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性(其他元素的几何属性和位置也会因此受到影响),然后再将计算的结果绘制出来。这个过程就是回流(也叫重排)。

重绘:当我们对 DOM 的修改导致了样式的变化、却并未影响其几何属性(比如修改了颜色或背景色)时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式(跳过了上图所示的回流环节)。这个过程叫做重绘。

我们来看一下重排重绘的触发情况:

  • 添加、删除、更新DOM节点
  • 通过display: none隐藏一个DOM节点-触发重排和重绘
  • 通过visibility: hidden隐藏一个DOM节点-只触发重绘,因为没有几何变化
  • 用户行为,例如调整窗口大小,改变字号,或者滚动。
  • 一些即时信息的获取:scrollTop。

实验

回到第二个问题,我和面试官的争执在于当JS引擎在执行超时的计算,动画效果是否执行

demo1

这里我写了一个旋转的动画,发现:transform rotate并不会触发重排重绘,动画一直执行。这么说是面试官对的了?

接着我试着写另外一个动画,颜色渐变的动画

demo2

很明显,由于颜色渐变会触发重绘,所以这个动画直接卡死,而rotate不会触发重绘所以,一直执行。

再来测试一下及时数据scrollTop

demo3

很怪异,不管你滚动条如何滚动,它好像只记下了当时最开始的位置,即时数据失效了,而且上述的滚动也会触发重绘重排,但是并没有受影响。个人猜测:**用户的动作是做了一些额外的优化,并没有占用main thread,而即时数据只记住了最开始的那一帧状态。**如果有其他见解欢迎留言私信。

结果

至此,回头看面试题,触发重排重绘的动画会直接卡死,而transform不会触发重排重绘,则继续执行,原因是合成层不与主线程共享。除此之外,用户的动作不受影响,而即时数据的获取则只会记住动画之前的那一帧状态。