缩小样式计算的范围并降低其复杂性[转]

简介: 通过添加和删除元素,更改属性、类或通过动画来更改 DOM,全都会导致浏览器重新计算元素样式,在很多情况下还会对页面或页面的一部分进行布局(即自动重排)。这就是所谓的计算样式的计算。

通过添加和删除元素,更改属性、类或通过动画来更改 DOM,全都会导致浏览器重新计算元素样式,在很多情况下还会对页面或页面的一部分进行布局(即自动重排)。这就是所谓的计算样式的计算。

计算样式的第一部分是创建一组匹配选择器,这实质上是浏览器计算出给指定元素应用哪些类、伪选择器和 ID。

第二部分涉及从匹配选择器中获取所有样式规则,并计算出此元素的最终样式。在 Blink(Chrome 和 Opera 的渲染引擎)中,这些过程的开销至少在目前是大致相同的:

用于计算某元素计算样式的时间中大约有 50% 用来匹配选择器,而另一半时间用于从匹配的规则中构建 RenderStyle(计算样式的表示)。 Rune Lillesveen, Opera / Blink 中的样式失效机制

  1. 降低选择器的复杂性;使用以类为中心的方法,例如 BEM。
  2. 减少必须计算其样式的元素数量。

降低选择器的复杂性

在最简单的情况下,您在 CSS 中引用只有一个类的元素:

.title {
  /* styles */
}

但是,随着项目的增长,将可能产生更复杂的 CSS,最终您的选择器可能变成这样:

.box:nth-last-child(-n+1) .title {
  /* styles */
}

为了知道是否需要应用样式,浏览器实际上必须询问“这是否为有 title 类的元素,其父元素恰好是负第 N 个子元素加上 1 个带 box 类的元素?”计算此结果可能需要大量时间,具体取决于所用的选择器和相应的浏览器。选择器的预期行为可以更改为一个类:

.final-box-title {
  /* styles */
}

您可能对该类的名称有疑问,但此工作对于浏览器而言要简单得多。在上一版本中,为了知道(例如)该元素是否为其类型的最后一个,浏览器首先必须知道关于其他元素的所有情况,以及其后面是否有任何元素会是第 N 个最后子元素,因为其类匹配,这可能比简单地将选择器与元素匹配的开销

减少要计算样式的元素数量

另一个性能考虑,对于_许多样式更新而言是更重要的因素_,即减少在元素更改时需要计算的工作量。

总体来说,计算元素的计算样式的最糟糕的开销情况是元素数量乘以选择器数量,因为需要对照每个样式对每个元素都检查至少一次,看它是否匹配。

注:以前曾经是这样:如果您改变了(例如)body 元素上的一个类,则该页的所有子元素将需要重新计算其计算样式。幸好情况不再是这样;对于更改时会导致重新计算样式的元素,某些浏览器维护一小组每个这种元素独有的规则。这意味着,根据元素在树中的位置以及所改变的具体属性,元素不一定需要重新计算。

样式计算可能经常是直接针对少量元素,而不是声明整个页面无效。在现代浏览器中,这往往不再是个问题,因为浏览器并不一定需要检查一项更改可能影响的所有元素。另一方面,较早的浏览器不一定针对此类任务进行了优化。应当尽可能减少声明为无效的元素的数量

注:如果您热衷于网页组件,有一点值得注意,样式计算在这方面稍有不同,因为默认情况下样式不会跨越 Shadow DOM 的边界,并且范围限于单个组件,而不是整个树。但是,总体来看,同样的概念仍然适用:规则简单的小树比规则复杂的大树会得到更高效地处理。

测量样式重新计算的开销

测量样式重新计算的最简单、最好的方法是使用 Chrome DevTools 的 Timeline 模式。首先,打开 DevTools,转至 Timeline 选项卡,选中记录并与您的网站交互。停止记录后,将看到下图所示情况。

image
顶部的条表示每秒帧数,如果看到柱形超过较低的线,即 60fps 线,则存在长时间运行的帧。
image
如果一些滚动之类的交互或其他交互时出现长时间运行的帧,则应当进一步审查。

如果出现较大的紫色块,如上例所示,请点击记录了解到更多细节。
image
在这次抓取中,有一个长时间运行的重新计算样式事件,其时间刚好超过 18 毫秒,并且恰好发生在滚动期间,导致用户体验到明显的抖动。

如果点击事件本身,将看到一个调用栈,精确指出了您的 JavaScript 中导致触发样式更改的位置。此外,您还获得样式受更改影响的元素数量(本例中刚好超过 400 个元素),以及执行样式计算所花的时间。您可以使用此信息来开始尝试在代码中查找修正点。

使用块、元素、修饰符

BEM(块、元素、修饰符)之类的编码方法实际上纳入了上述选择器匹配的性能优势,因为它建议所有元素都有单个类,并且在需要层次结构时也纳入了类的名称:

.list { }
.list__list-item { }

如果需要一些修饰符,像在上面我们想为最后一个子元素做一些特别的东西,就可以按如下方式添加:

.list__list-item--last-child {}

如果您在寻找一种好方法来组织您的 CSS,则 BEM 真的是个很好的起点,不仅从结构的角度如此,还因为样式查找得到了简化。

如果不喜欢 BEM,还可使用其他方法来组织您的 CSS,但是应评估其性能注意事项等。

目录
相关文章
|
6月前
模块功能降低复杂度
模块功能降低复杂度
46 3
|
1月前
|
测试技术 UED
软件测试中的“灰盒”方法:一种平衡透明度与效率的策略
在软件开发的复杂世界中,确保产品质量和用户体验至关重要。本文将探讨一种被称为“灰盒测试”的方法,它结合了白盒和黑盒测试的优点,旨在提高测试效率同时保持一定程度的透明度。我们将通过具体案例分析,展示灰盒测试如何在实际工作中发挥作用,并讨论其对现代软件开发流程的影响。
|
4月前
|
图形学 开发者
U3D开发进阶:精细调整Collider与优化碰撞检测性能
【7月更文第11天】在Unity 3D(简称U3D)开发过程中,精确控制Collider(碰撞器)的设置与合理利用Layer Collision Matrix(层级碰撞矩阵)对于提升游戏性能、优化物理模拟至关重要。本文将深入探讨这两项技术的应用,通过实际案例和代码示例,帮助开发者构建更加高效、流畅的游戏体验。
511 2
|
6月前
|
机器学习/深度学习 JSON 物联网
ORPO偏好优化:性能和DPO一样好并且更简单的对齐方法
ORPO是另一种新的LLM对齐方法,这种方法甚至不需要SFT模型。通过ORPO,LLM可以同时学习回答指令和满足人类偏好。
321 0
|
6月前
|
设计模式 算法
我确实遇到过优化代码却导致过度设计的状况
我确实遇到过优化代码却导致过度设计的状况
44 10
|
6月前
|
算法
【MFAC】基于紧格式动态线性化的无模型自适应迭代学习控制
【MFAC】基于紧格式动态线性化的无模型自适应迭代学习控制
【MFAC】基于紧格式动态线性化的无模型自适应迭代学习控制
|
程序员 测试技术 API
过度设计有意义吗
看着自己每次根据设计原则及模式的代码重构,虽效果不错,但也自省:如果我的每段代码都这么写,是不是过度设计?把握设计的度,需长久锤炼。行业也总结了很多原则,帮助我们把握设计的度。它们是一种思考方法、一种行为准则。
97 0
An动画优化之补间形状与传统补间的优化
An动画优化之补间形状与传统补间的优化
909 0
An动画优化之补间形状与传统补间的优化
|
算法
几种算法的时间和控件复杂度
几种算法的时间和控件复杂度
84 0
几种算法的时间和控件复杂度