BEM。如同前端开发领域中几乎所有技术一样,使用 BEM 格式编写 CSS 可能会引起两极分化。但在我的 Twitter 社交圈中,它至少是更受欢迎的 CSS 方法之一。
我个人认为 BEM 很好,我认为你应该使用它。但我也能理解你为什么可能不使用它。
无论你对 BEM 的看法如何,它都提供了一些好处,最大的好处是它有助于避免 CSS 级联中的特异性冲突。这是因为,如果使用得当,任何以 BEM 格式编写的选择器都应该具有相同特异性分数 (0,1,0)。多年来,我已经为许多大型网站设计了 CSS(想想政府、大学和银行),正是这些大型项目让我发现 BEM 真正闪耀的地方。当您确信正在编写或编辑的样式不会影响网站的其他部分时,编写 CSS 会更有趣。
实际上,在某些情况下,添加特异性被认为是完全可以接受的。例如::hover
和 :focus
伪类。它们的特异性分数为 0,2,0。另一个是伪元素——例如 ::before
和 ::after
——它们的特异性分数为 0,1,1。不过,在本文的其余部分,让我们假设我们不希望出现任何其他特异性蔓延。?
但我并不是真的想向你推销 BEM。相反,我想谈谈我们如何将它与现代 CSS 选择器(例如 :is()
、:has()
、:where()
等)结合使用,以获得更强的级联控制。
CSS 选择器级别 4 规范为我们提供了一些强大且相对较新的选择元素的方法。我最喜欢的一些包括 :is()
、:where()
和 :not()
,它们都受所有现代浏览器支持,并且现在几乎可以在任何项目中安全使用。
:is()
和 :where()
基本相同,只是它们对特异性的影响不同。具体来说,:where()
的特异性分数始终为 0,0,0。是的,即使是 :where(button#widget.some-class)
也没有特异性。同时,:is()
的特异性是其参数列表中特异性最高元素的特异性。因此,我们已经有了可以使用的两个现代选择器之间在级联整理方面的区别。
功能强大的 :has()
关系伪类也正在迅速获得浏览器支持(在我看来,这是自 Grid 以来 CSS 最大的新功能)。但是,在撰写本文时,:has()
的浏览器支持还不够好,还不能用于生产环境。
让我在我的 BEM 中添加一个伪类……
<code>/* ❌ 特异性分数:0,2,0 */ .something:not(.something--special) { /* 所有 something 的样式,除了特殊的 something */ }</code>
糟糕!看到那个特异性分数了吗?记住,使用 BEM,我们理想情况下希望我们的选择器都具有 0,1,0 的特异性分数。为什么 0,2,0 不好?考虑一个类似的扩展示例:
<code>.something:not(a) { color: red; } .something--special { color: blue; }</code>
即使第二个选择器在源代码顺序中最后出现,第一个选择器更高的特异性 (0,1,1) 也会胜出,并且 .something--special
元素的颜色将设置为红色。也就是说,假设您的 BEM 编写正确,并且所选元素在 HTML 中同时应用了 .something
基类和 .something--special
修饰符类。
如果使用不当,这些伪类可能会以意想不到的方式影响级联。正是这些不一致之处可能会在以后造成麻烦,尤其是在更大更复杂的代码库中。
还记得我刚才说的 :where()
及其特异性为零的事实吗?我们可以利用这一点:
<code>/* ✅ 特异性分数:0,1,0 */ .something:where(:not(.something--special)) { /* 等 */ }</code>
此选择器的第一部分(.something
)获得其通常的特异性分数 0,1,0。但是 :where()
——以及其中的所有内容——的特异性为 0,不会进一步增加选择器的特异性。
那些不像我那样关心特异性的人(公平地说,可能很多人如此)在嵌套方面一直做得很好。通过一些随意的键盘敲击,我们可能会得到这样的 CSS(请注意,我使用 Sass 简洁起见):
<code>.card { ... } .card--featured { /* 等 */ .card__title { ... } .card__title { ... } } .card__title { ... } .card__img { ... }</code>
在这个例子中,我们有一个 .card
组件。当它是一个“特色”卡片(使用 .card--featured
类)时,卡片的标题和图片需要不同的样式。但是,正如我们现在所知,上面的代码导致的特异性分数与我们系统的其余部分不一致。
一个顽固的特异性狂热者可能会这样做:
<code>.card { ... } .card--featured { ... } .card__title { ... } .card__title--featured { ... } .card__img { ... } .card__img--featured { ... }</code>
还不错,对吧?坦率地说,这是漂亮的 CSS。
不过,HTML 也有缺点。经验丰富的 BEM 作者可能痛苦地意识到,需要复杂的模板逻辑来有条件地将修饰符类应用于多个元素。在这个例子中,HTML 模板需要有条件地将 --featured
修饰符类添加到三个元素(.card
、.card__title
和 .card__img
),但在现实世界的例子中可能更多。有很多 if 语句。
:where()
选择器可以帮助我们编写更少的模板逻辑——以及更少的 BEM 类——而不会增加特异性级别。
<code>.card { ... } .card--featured { ... } .card__title { ... } :where(.card--featured) .card__title { ... } .card__img { ... } :where(.card--featured) .card__img { ... }</code>
以下是 Sass 中的相同内容(注意尾随的与号):
<code>.card { ... } .card--featured { ... } .card__title { /* 等 */ :where(.card--featured) & { ... } } .card__img { /* 等 */ :where(.card--featured) & { ... } }</code>
您是否应该选择这种方法而不是将修饰符类应用于各种子元素,这取决于个人喜好。但至少 :where()
现在给了我们选择!
我们生活在一个不完美的世界里。有时您需要处理超出您控制范围的 HTML。例如,注入您需要设置样式的 HTML 的第三方脚本。这些标记通常不是使用 BEM 类名编写的,在某些情况下,这些样式根本不使用类,而是 ID!
同样,:where()
也可以帮我们解决这个问题。此解决方案略显笨拙,因为我们需要引用 DOM 树中我们知道存在的某个元素的类。
<code>/* ❌ 特异性分数:0,2,0 */ .something:not(.something--special) { /* 所有 something 的样式,除了特殊的 something */ }</code>
引用父元素感觉有点冒险和限制性。如果该父类发生更改或由于某种原因不存在怎么办?更好的(但可能同样笨拙的)解决方案是改用 :is()
。记住,:is()
的特异性等于其选择器列表中最特异的选择器。
因此,我们可以使用 :is()
引用一个我们知道(或希望!)存在的类,而不是像上面的例子那样使用 :where()
引用它。
<code>.something:not(a) { color: red; } .something--special { color: blue; }</code>
始终存在的 body
将帮助我们选择 #widget
元素,并且在同一个 :is()
中存在 .dummy-class
类会使 body
选择器具有与类相同的特异性分数 (0,1,0)… 并且使用 :where()
确保选择器不会比这更具体。
这就是我们如何利用 :is()
和 :where()
伪类的现代特异性管理功能以及使用 BEM 格式编写 CSS 时获得的特异性冲突预防功能。在不久的将来,一旦 :has()
获得 Firefox 支持(在撰写本文时,它目前在标志后面受支持),我们可能希望将其与 :where()
配对以撤消其特异性。
无论您是否完全采用 BEM 命名,我希望我们都能同意选择器特异性的一致性是一件好事!
以上是用BEM和Modern CSS选择器驯服级联的详细内容。更多信息请关注PHP中文网其他相关文章!