不知道你有没有注意到,在基于Webkit的浏览器上执行某些CSS操作时页面会出现不流畅或者闪一下的情况,尤其是执行CSS动画的时候,那你之前可能已经听过“硬件加速”这个专业术语了。
CPU&GPU&硬件加速
简单来说硬件加速意味着浏览器会帮你把一部分对渲染页面来说较繁重的任务交给GPU(Graphics Processing Unit),而不是一股脑都交给CPU(Central Processing Unit )去处理,当这部分CSS操作得到硬件加速时,可以使页面渲染速度更快。
CPU在电脑主板上,就像是计算机的”大脑“,CPU几乎会做所有事,GPU位于计算机的显卡上,主要用做图形渲染,此外,GPU是专为执行图形渲染所需的复杂数学和几何计算而设计的,所以将一些操作移到GPU上去处理可以带来巨大的性能提升并且减轻了CPU的压力,在移动端尤为明显。
硬件加速(又名GPU加速)依赖于浏览器在渲染页面时使用的分层模型,当对页面上的元素执行某些操作(例如3D变换),对应元素会被提升到到自己的图层,独立于页面的其余部分的呈现并在后期合成(绘制到屏幕上),这样单独把元素隔离,可以使得当页面上只有这个元素发生变换(transform)的时候其余元素不需要重新渲染,从而带来速度提升的优点,值得一提的是,只有3D变换才有资格拥有自己的图层,2D变换则没有。
CSS animation,transform,transition这些属性并不会自动被硬件加速,而是由浏览器的渲染引擎去执行的,但是一些浏览器通过某些属性提供硬件加速,从而提升页面的渲染性能。例如CSS中的opacity属性是少数几个可以被浏览器认定为可被硬件加速的属性之一,因为GPU可以很容易的实现。一般来说,任何想要通过CSS transition或动画淡化不透明度的图层的行为,浏览器会把它丢给GPU去处理从而提高处理速度。所有的CSS属性中opacity是性能最好属性的之一,其他常见的硬件加速操作是CSS translate3d变换。
硬件加速操作会创建所谓的合成层,合成层会被上传到GPU并由GPU合成,但是这种方法去创建图层并不是万能的,图层创建可以加快页面加载速度,但会带来其他的成本:它会占用系统RAM和GPU上的内存(限于移动设备),并且很多时候都会带来不良影响(特别是在移动设备上),所以这种方法要合理的去使用,你必须要清楚的知道使用这种方式是不是真的可以提高页面性能,不能使这个操作反而成了影响页面性能的瓶颈。
除了这种创建图层的方法,CSS引入了一个新的属性,它允许我们提前告知浏览器可能会对元素进行哪些操作,让浏览器去优化并提前处理那些潜在的比较消耗性能的操作比如在动画开始之前,提前处理元素的动画行为。这个属性就是will-change。
will-change属性允许你提前通知浏览器你可能会对某个元素做什么类型的操作,以便于浏览器在需要的时候采取适当的优化方案。因此,避免了可能对页面的响应性产生负面影响的非必要成本,使元素可以更快地呈现。渲染并快速更新,从而获得更流畅的体验。
举个例子,当对元素使用CSS transform时,元素及其内容可能会提升为图层,如之前所言,之后会将他们合成(composited)(绘制在屏幕上),但是将一个元素提升到一个新图层是很消耗性能的,这可能会使transform动画的开始延迟明显的几分之一秒,从而引起明显的“闪烁”。
为了避免这种情况发生,我们可以提前告知浏览器,让浏览器可以提前做准备,那么当同样的操作发生时,因为元素的图层已准备就绪,然后就可以立刻执行转换动画,从而渲染元素并快速更新页面。
不要使用will-change去尝试优化一切操作,有时候相反会带来反作用,要更加聪明且合理的使用它,will-change也有一些无法直接检测到的副作用,毕竟这也只是一种和浏览器后台通话的方式而已,不能指望它为你做所有事情,因为使用此属性时,请牢记以下几点,以确保在最大程度上利用该属性,同时避免因滥用该属性而带来的危害。
不要将 will-change 应用到太多元素上:浏览器已经尽力尝试去优化一切可以优化的东西了。有一些更强力的优化,如果与 will-change 结合在一起的话,有可能会消耗很多机器资源,如果过度使用的话,可能导致页面响应缓慢或者消耗非常多的资源。
有节制地使用:通常,当元素恢复到初始状态时,浏览器会丢弃掉之前做的优化工作。但是如果直接在样式表中显式声明了 will-change 属性,则表示目标元素可能会经常变化,浏览器会将优化工作保存得比之前更久。所以最佳实践是当元素变化之前和之后通过脚本来切换 will-change 的值。
不要过早应用 will-change 优化:如果你的页面在性能方面没什么问题,则不要添加 will-change 属性来榨取一丁点的速度。will-change 的设计初衷是作为最后的优化手段,用来尝试解决现有的性能问题。它不应该被用来预防性能问题。过度使用 will-change 会导致大量的内存占用,并会导致更复杂的渲染过程,因为浏览器会试图准备可能存在的变化过程。这会导致更严重的性能问题。
给它足够的工作时间:这个属性是用来让页面开发者告知浏览器哪些属性可能会变化的。然后浏览器可以选择在变化发生前提前去做一些优化工作。所以给浏览器一点时间去真正做这些优化工作是非常重要的。使用时需要尝试去找到一些方法提前一定时间获知元素可能发生的变化,然后为它加上 will-change 属性。