谈谈will-change这个性能优化的利器
1一、will-change是做什么的呢
CSS 是用来描述结构化文档(如HTML、XML)怎样渲染的语言。
CSS渲染器在渲染属性前,会有个准备的过程。有些属性需要css渲染器事先做很多准备才能实现渲染。这就容易导致页面出现卡顿,交互体验不好等问题。
如果设置了 will-change 属性,那么浏览器就可以提前知道哪些元素的属性将会改变,提前做好准备。待需要改变元素的时机到来时,就可以立刻实现它们。从而避免卡顿等问题。
然而,将元素提取到一个新层,相对来说是代价较高的操作。这可能使 transform 动画延迟几百毫秒。
二、怎么用呢
下面是一个使用脚本应用 will-change 属性的例子:
var el = document.getElementById('element');
// 当鼠标移动到该元素上时给该元素设置 will-change 属性
el.addEventListener('mouseenter', hintBrowser);
// 当 CSS 动画结束后清除 will-change 属性
el.addEventListener('animationEnd', removeHint);
function hintBrowser() {
// 填写上那些你知道的,会在 CSS 动画中发生改变的 CSS 属性名们
this.style.willChange = 'transform, opacity';
}
function removeHint() {
this.style.willChange = 'auto';
}
语法说明
Formal syntax: auto | <animateable-feature>#
<animateable-feature> = scroll-position | contents | <custom-ident>
will-change: auto
will-change: scroll-position
will-change: contents
will-change: transform //
的例子
will-change: opacity //
的例子
will-change: left, top //
的例子
will-change: unset
will-change: initial
will-change: inherit
auto
浏览器会根据情况,自行进行优化。
scroll-position
表示开发者将要改变元素的滚动位置。
content
表示开发者将要改变元素的内容。
注意:这个值会被应用到它所声明元素的子节点。在文档树较高的节点上使用,可能会对页面性能造成很大的影响。尽量在文档树最末端使用。
表示开发者将要改变的元素属性。如果给定的值是缩写,则默认被扩展全。
三、使用时须注意的几点
1、不要在过多的属性和元素上使用 will-change
* { will-change: transform, opacity /*, ...*/; }
你可能会觉得这么做不错,就可以优化一切属性了。
其实并非如此。
will-change 可能会引发一些十分耗费资源的优化措施。如果像上边这样给所有元素都添加,可能会使页面变慢,甚至崩溃。
2、在元素属性变化完成后最好移除 will-change 属性
有些情况,可以不移除。
比如,给页面中少量的元素使用 will-change 属性能使交互体验更好。
body > .sidebar {
will-change: transform;
/*当鼠标移动到侧边栏时,会有滑动效果*/
}
因为只在很少的元素上使用,所以它所能产生的副作用可以忽略不计。
当变化很频繁时也可以不移除。例如,鼠标移动产生的变化,或者持续存在的动画效果。此时设置 will-change 属性,其实就是在提示浏览器,这些元素会持续或有规律的发生变化,要保持对它们的优化。
.cats-flying-around-the-screen {
will-change: left, top;
}
3、给 will-change 属性足够的时间做准备
如果在动画开始的那一刻才添加 will-change 属性,是没有优化效果的。一些优化是需要充分的准备时间的.如果没有足够的时间,那 will-change 所能提高的性能也就无从谈起。所以要找到添加 will-change 属性的时机。
.element { transition: opacity .2s; opacity: 1; }
.element:hover { will-change: opacity; }
.element:active { opacity: .3; }
如果变化是发生在触发 hover 事件时,上边的做法就无法起到优化作用了。但还是可以找到恰当的时机的。比如在祖先元素上设置 will-change 属性,就可以给浏览器预留足够的准备时间。
.element { transition: opacity .2s; opacity: 1; }
.container:hover > .element { will-change: opacity; }
.element:hover { opacity: .3; }
兼容性一览
参考资料
[1] CSS Will Change Module Level 1: https://www.w3.org/TR/css-will-change-1/#propdef-will-change
[2] MDN will-change 中文版: https://developer.mozilla.org/zh-CN/docs/Web/CSS/will-change