样式计算一般是直接对那些目标元素执行,而不是对整个页面执行。在现代浏览器中,样式计算进一步被优化,因为浏览器不会检查所有受到样式变化影响的元素。而以前的浏览器对于这种情况的处理没有进行这种优化。因此,你最好尽可能减少需要执行样式计算的元素的个数。
Note
- 如果你对Web Components很感兴趣,那这篇文章对你就没什么意义了。因为Web Components中的样式计算不会跨越Shadow DOM范围,仅在单个的Web Component中进行,而不是在整个页面的DOM树上进行。但从整体上看,本质是一样的:对于样式计算来说,范围越小、规则越简单的话,处理效率越高。
评估样式计算的成本
最简单最好的评估样式计算成本的方式就是使用Chrome DevTools的Timeline功能。打开DevTools,选择Timeline标签,点击左上角红色record按钮,然后在页面上做一些互动操作。再点击一次那个红色按钮结束记录,你就会看到类似下图的画面:
顶部的横线表示该页面每秒渲染的帧数,如果你看到有柱状条超过了下面的那条横线,也就是表示60fps的那条线,那就说明你的页面里有运行时间过长的帧。
如果页面在与用户交互的过程(比如页面滚动)中有运行时间过长的帧,那么我们就得对这些帧好好分析一下了。
如果你看到了很高的紫色柱状条,就像下图所示。那么点击那个紫色条,你会看到更多细节信息。
在细节信息中,我们可以看到一个耗时很长的样式计算事件,该事件的执行耗时超过了18毫秒。不巧的是,它正好是在页面滚动过程中发生的,因此给用户带来了一个很明显的卡顿效果。
再点击一下JavaScript事件,你就会看到一个JavaScript事件调用栈。在这个栈中你能准确找到是哪个JavaScript事件触发了样式改动。另外,你还能看到这个样式改动影响到的元素个数(在本示例中这个数字超过400)、样式计算耗时多久。这些信息有助于你寻找改进代码的方法。
使用块、元素、修饰语
以BEM (Block, Element, Modifier)的方式编写CSS代码,能达到最好的样式计算的性能,因为这种方式建议对每个DOM元素都只使用一个样式class。对于需要层级结构的情况,只需要把层级信息合并到class名里面:
.list { }
.list__list-item { }
如果你需要用修饰语,比如前面那个需要获取最后一个子元素的例子,你可以这样处理:
.list__list-item--last-child {}
如果你正在寻找一种更好的组织CSS代码的方式,BEM是一个很好的选择,不管是在代码结构、还是样式查找速度方面,它的表现都是很棒的。
如果你不喜欢用BEM,当然还有其他编写CSS的方式可用,不过在使用它之前,你得好好评估一下它在性能方面的表现。