will-change属性的正确用法与性能优化实践

设计师芳宁 优化 阅读 1,018
赞 15 收藏
二维码
手机扫码查看
反馈

我的写法,亲测靠谱

先说结论吧,我一般这样用 will-change:

will-change属性的正确用法与性能优化实践

.modal {
  transform: translateZ(0);
  will-change: transform, opacity;
}

上面这段代码是我优化弹窗动画时常用的写法。给 .modal 加上 will-change 后,页面渲染性能提升很明显,尤其是在低端设备上。

这里重点说下为什么这么写。首先,transformopacity 是最常被优化的属性,它们不会触发重排,只会触发重绘或者合成。其次,提前声明这两个属性的变化意图,可以让浏览器提前做好优化准备。

至于为什么要加 translateZ(0),这是个小技巧。它能强制开启硬件加速,让元素进入合成层。但要注意,别滥用这个技巧,后面我会详细讲到。

这几种错误写法,别再踩坑了

说实话,我在项目里见过太多 will-change 的错误用法。最常见的就是这种:

* {
  will-change: all;
}

千万别这么干!这种写法相当于告诉浏览器:所有元素的所有属性都可能变化。结果就是,浏览器根本不知道该怎么优化,反而可能导致性能更差。

还有这种:

.button {
will-change: transform;
transition: background-color 0.3s ease;
}
`>
<p>问题出在 <code>will-change</code> 声明的属性和实际动画属性不一致。这里声明的是 <code>transform</code>,但实际动画是 <code>background-color</code>,这种写法纯属浪费。</p>

&lt;p&gt;最夸张的是我遇到过一个同事,直接在 HTML 根元素上写了:&lt;/p&gt;</code></pre>html
<body style="will-change: scroll-position">
<pre class="pure-highlightjs line-numbers language-none"><code class="no-highlight language-none">&lt;p&gt;这位老哥想优化滚动性能,但这种全局性的声明会让整个页面都进入优化状态,导致内存占用飙升。最后我们不得不回滚这个改动。&lt;/p&gt;

&lt;h2&gt;实际项目中的坑&lt;/h2&gt;
&lt;p&gt;记得去年做移动端项目的时候,就因为 will-change 踩过一个大坑。当时为了优化轮播图效果,我给每个图片都加了:&lt;/p&gt;</code></pre>css
.slider-item {
will-change: transform;
}
<pre class="pure-highlightjs line-numbers language-none"><code class="no-highlight language-none">&lt;p&gt;看起来挺合理对吧?结果上线后发现,在某些 Android 机上,页面滚动变得特别卡顿。折腾了半天才发现,问题就出在 will-change 上。&lt;/p&gt;

&lt;p&gt;原因其实很简单:&lt;strong&gt;过度使用 will-change 会导致 GPU 内存占用过高&lt;/strong&gt;。每张图片都单独声明优化,等于让浏览器为每个元素都分配独立的合成层。对于低端设备来说,这就是灾难。&lt;/p&gt;

&lt;p&gt;后来我把代码改成这样:&lt;/p&gt;</code></pre>css
.slider-container {
will-change: transform;
}
>
<p>只在容器上声明一次优化意图,性能问题马上就解决了。所以建议大家记住这点:能用父级优化的,就别在子元素上分散声明。</p>

<h2>一些容易忽视的细节</h2>
<p>说几个小经验。第一,will-change 不要过早声明。有些同学喜欢在样式初始化的时候就加上,比如:</p>
<pre class="pure-highlightjs line-numbers language-css"><code class="no-highlight language-css">.modal {
will-change: transform;
}
&gt;
&lt;p&gt;这种写法的问题是,浏览器会一直保持优化状态,即使元素根本没有发生任何变化。正确的做法是,在需要动画之前才动态添加:&lt;/p&gt;</code></pre>javascript
const modal = document.querySelector('.modal');
modal.style.willChange = 'transform';
// 动画结束后移除
setTimeout(() => {
modal.style.willChange = 'auto';
}, 1000);
<pre class="pure-highlightjs line-numbers language-none"><code class="no-highlight language-none">&lt;p&gt;第二,别忘了及时清理。就像上面的代码展示的那样,动画结束后要把 will-change 改回 auto。不然浏览器会一直为这个元素保留优化资源,造成浪费。&lt;/p&gt;

&lt;p&gt;第三,谨慎处理复杂的动画场景。如果一个元素同时有多个属性需要优化,比如:&lt;/p&gt;</code></pre>css
.complex {
will-change: transform, opacity, filter, background-color;
}
`>

这种情况下,最好拆分动画步骤,或者考虑用 CSS 动画替代 JavaScript 动画。因为同时优化多个复杂属性,很容易超出设备的处理能力。

结尾唠叨几句

以上就是我这些年使用 will-change 总结的一些实战经验。说到底,这个属性就像是给浏览器的一个提示,但千万别把它当成万能药。用得好能显著提升性能,用不好反而会拖累页面。

我现在的原则是:按需使用,及时清理,避免过度。如果你也有类似的经验或者更好的实践方案,欢迎在评论区交流。前端优化这条路,大家一起摸索着走。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论