使用 will-change 来提升浏览器渲染效果

287 查看

今天偶然在youtube上看到一段视频,讲诉的是作者在他的网页中用到了background-attachment: fixed;来实现背景图固定效果,但是这种是实现方式导致页面在滚动时,会出现页面卡顿的现象,这很明显降低了用户的体验。 下面介绍通过will-change来部分规避这个问题。

原始版本

让我们先来重现一下产生问题的场景,我写了一个简单的DEMO:
http://codepen.io/CodingMonkeyzh/pen/zGWyYM

可以看到, 我们在.first块中放了一张背景图片,并且通过background-attachment:fixed来实现背景图固定的效果,你可以滚动页面感受一下效果,是不是很酷炫?下面是核心的样式:

.first {
  background: url('http://placehold.it/800x500') no-repeat center center;
  background-attachment: fixed;
  background-size: cover;
}

通过这种方式实现的效果表现力如何呢? 打开Chrome DevTool 的 Timeline来调试一下:

这看上去并不怎么样, 很多时候都掉到了30fps以下。

改进版本

下面,我们试试用will-change来做一下浏览器优化。同样,我基于前面的例子,放弃了之前的实现方式,加入了will-change来优化浏览器渲染效果,这是我的DEMO:
http://codepen.io/CodingMonkeyzh/pen/YXadXP

我们没有在使用background-attachment: fixed;来实现这个效果,取而代之的是,利用:before,在其中放入一张position:fixed;的背景图片,然后用will-change: transform做浏览器渲染优化。下面是核心代码:

.first::before {
  content: ' ';
  position: fixed;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  background: url('http://placehold.it/800x500') no-repeat center center;
  background-size: cover;
  will-change: transform;
  z-index: -1;
}

那通过这种方式进行优化,表现力究竟如何呢?我们采用同样的方式对页面进行调试,结果如下:

可以看到,页面的帧率明显得到了提升!页面滚动时变得流畅了许多。

那么,will-change到底是什么?

will-change 为web开发者提供了一种告知浏览器该元素会有哪些变化的方法,这样浏览器可以在元素属性真正发生变化之前提前做好对应的优化准备工作。 这种优化可以将一部分复杂的计算工作提前准备好,使页面的反应更为快速灵敏。

will-change 的兼容性

然后,will-change 目前还在实验结果,浏览器的支持度不尽人意,IE目前没有支持该属性,Chrome 36+, Firefox 38+ 也才开始支持这一属性。

注意事项

正如MDN 上面提到的那样will-change虽然可以提升渲染效果,但是我们需要引起注意:

  • 不要将 will-change 应用到太多元素上:浏览器已经尽力尝试去优化一切可以优化的东西了。有一些更强力的优化,如果与 will-change 结合在一起的话,有可能会消耗很多机器资源,如果过度使用的话,可能导致页面响应缓慢或者消耗非常多的资源。
  • 有节制地使用:通常,当元素恢复到初始状态时,浏览器会丢弃掉之前做的优化工作。但是如果直接在样式表中显式声明了 will-change 属性,则表示目标元素可能会经常变化,浏览器会将优化工作保存得比之前更久。所以最佳实践是当元素变化之前和之后通过脚本来切换 will-change 的值。
  • 不要过早应用 will-change 优化:如果你的页面在性能方面没什么问题,则不要添加 will-change 属性来榨取一丁点的速度。 will-change 的设计初衷是作为最后的优化手段,用来尝试解决现有的性能问题。它不应该被用来预防性能问题。过度使用 will-change 会导致大量的内存占用,并会导致更复杂的渲染过程,因为浏览器会试图准备可能存在的变化过程。这会导致更严重的性能问题。
  • 给它足够的工作时间:这个属性是用来让页面开发者告知浏览器哪些属性可能会变化的。然后浏览器可以选择在变化发生前提前去做一些优化工作。所以给浏览器一点时间去真正做这些优化工作是非常重要的。使用时需要尝试去找到一些方法提前一定时间获知元素可能发生的变化,然后为它加上 will-change 属性。

参考: