[译] How camel-cased classes are way better than kebab-cased classes

0 阅读8分钟

原文信息


声明

代码 100% 还原

部分地方同时有原文译文。


我发现一种比 BEM 更好的 class 写法。

它就是——驼峰命名。

你别说话,先听我说:

  • 我不是专职写 JavaScript 的。
  • 我是个 CSS 仔,我喜欢 CSS。
  • 我爱搞优化。

驼峰真的比烤串写法更好——尤其是写模块化 CSS。

跟着我的引导,先从历史悠久的 BEM 开始。

传统 BEM

BEM 有 Block 块、Element 元素、Modifier 修饰符。

  • Block 是组件名
  • Element 是组件的子元素
  • Modifier 用于修改 Block 或 Element 的基本样式。

这篇文章能帮助新手快速了解 BEM。

传统 BEM 的格式:

  • Block:烤串命名法的组件:block
  • Element:block 后跟两道下划线,再写元素名:block__element
  • Modifier:block 或 element 后面两道横杠,再写修饰符:block--modifier 或 block__element--modifier

写出来像这样:

.block { } /* 组件 */
.block__element { } /* 子元素 */
.block--modifier { } /* 被修饰的块 */
.block__element--modifier { } /* 被修饰的元素 */

对应的 HTML:

<div class="block">
  <div class="block__element">...</div>
  <div class="block__element2">...</div>
</div>

现在,BEM 已经成为一个出色的系统,遵守它的规则,改动其他组件的元素和修饰符样式,就不会出错,能让开发人员在处理大型 CSS 代码库时保持理智。

BEM 毕竟历史悠久,不太能适应现在的玩法。

现在常见的是工具类(Tailwind 帮它变得非常流行)。

如果你想在 BEM 项目使用工具类,就会注意到它根本不适合 BEM 范式……因为 BEM 没有设计成可扩展的。

想沿用 BEM 惯例,添加工具类的最佳方法是在块 (block)、元素 (element) 或修饰符 (modifier) 的前面写 u-。这种做法叫命名空间。

/* 工具类,假设 block 是通用名称 */
.u-block {}

如果进一步扩展命名空间的概念,还有更多能用的,比如 Brad Frost 的原子设计系统,里面有原子 (atom) 和分子 (molecules)。如果你要实现原子设计系统,你可以用 a- 表示原子,用 m- 表示分子。

我在这篇关于命名空间的文章做了更详细的阐述。

/* 应用原子设计系统命名划分 */
.a-block {}
.m-block {}

如你所见,BEM 是一个很棒的系统。

元素和修饰符写着写着就变丑了,这是唯一缺憾。

这里有个常见的例子:

<div class="block block--modifier">
  <div class="block__element">...</div>
  <div class="block__element2 block__element2--modifier">...</div>
</div>

这段 HTML 你看着难受吧?俺也一样。这就是我总在找更好方案的原因。

我很高兴,找到了一个更好的 CSS 命名系统。

为了简单,就叫它驼峰 CSS。

驼峰 CSS

驼峰 CSS 也有块 (block)、元素 (element)、修饰符 (modifier)。但写法不同。

  • Block:用大驼峰命名,就像 "Block"。
  • Element:用驼峰命名,比如 "element"
  • Modifier:带 m 前缀的修饰符, 就像:.mModifier
.Block {} /* 组件 */
.element {} /* 子元素 */
.mModifier {} /* 修饰块或元素 */

CamelCSS 现在看着普通,实则不然

这其中有:

  1. block 就像框架的组件
  2. 可读性更好
  3. 可扩展性更好
  4. 能组合类名
  5. 组件命名更容易

一起探讨这五项益处。

好处 1:Block 就像框架里的组件

用 Svelte、Vue 或 React 构建组件,组件通常是大驼峰命名。

一张包含组件文件夹的图片。项目都以大驼峰的格式进行标注。

组件文件夹的图片。每项都是大驼峰命名。

新 BEM 语法只需按大驼峰给各个块 (blocks) 命名,再也不用为了组件的大驼峰烤串风格转换分心。精力可以投入到打磨代码质量这种更有意义的事情。

好处2:可读性更好

看完这个例子,你就明白为什么驼峰 CSS 比 BEM 更出色——尤其在使用命名空间的时候。

瞧瞧下面的代码,再想想:哪一段更容易阅读和理解呢?

<!-- 传统 BEM -->
<h2 class="u-text-center u-center">Some Text Here</h2>


<!-- 驼峰 CSS -->
<h2 class="uTextCenter uCenter">Some Text Here</h2>

如果按照上述说明读写代码,就会发现 驼峰 CSS 比 BEM 更好理解。

因为:在驼峰 CSS 示例中,直接能认出两个类。(但仔细查看 BEM 会发现,不同的部分有 4-5 个)。

我们潜意识里会将破折号、下划线和空格视为某种分隔符。所以 u-text-center 看着像三个独立的部分,uTextCenter 显然就是一个整体。

所以,驼峰 CSS 比传统的「烤串」类名可读性更好。

好处 3:驼峰 CSS 有极强的可扩展性

上面的示例已经展示了如何添加实用类,这看起来非常自然。

这意味着驼峰 CSS 具有可扩展性,我们可以对其进行扩展,以纳入其他类型的类

以下是我一直在思考的一些例子:

  • Icons 图标: 前缀 i
  • Typography classes 排版类名: 前缀 t
  • Containers 容器: 加一个 Container 后缀
.iStar {} /* Icon class 图标类名 */
.tStylish {} /* Typography class 排版类名 */
.BlockContainer {} /* Container class 容器类名 */

和容器查询一起用,容器类就变得极其有用!这是我的最佳实践经历之一。我在后续文章会进一步探讨关于容器查询。

那么今天,让我们把注意力集中在驼峰 CSS 上。

好处 4:可以组合

这是驼峰 CSS 最大的好处。

类名能组合,就说明能将多个类名加到同一个元素上。上述实用示例就是类名组合的一种简单形式。

<h2 class="uTextCenter uCenter">Some Text Here</h2>

现在我们可以更进一步,将「块」和「修饰符」一起应用到同一个元素上。

<div class="Block mModifier">...</div>

这意义重大,因为我们在 CSS 中可以分别对 BlockmModifier 进行设置。

以下是 CSS 应该采用的正确写法。

.Block { } /* 块 */
.Block.mModifier { } /* 修饰符 */

写 CSS 时将 mModifier 类附加到到 .Block 上是很有必要的,原因有二:

  1. 单独使用修饰符没意义,必须和修饰的内容一起用。将 mModifier 附加给 Block,等于为 Modifier 名称开辟了使用空间,使其能够应用于其他块。
  2. 这种更高的权重确保了 .mModifier 的样式总是会覆盖 .Block 的,因此我们就不必担心 CSS 类名的书写顺序了。

让我给你举一个实际的例子,说明这种做法是如何被应用的。

在这个示例中,我有一个 BasicGrid 块名。正如其名称所示,它会创建一个基本网格布局(我曾多次在 Magical Dev School’s website 的网站中使用过这种布局)。

然后,我有三个修饰符:

  • mOneColumn:此表示单列网格。
  • mTwoColumns:此表示双列网格。
  • mGapWide:此会增大双列网格中各元素之间的间距。

Here’s how I used these three modifiers.

以下是我是如何运用这三种修饰符的。

<div class="BasicGrid mOneColumn"></div>
<div class="BasicGrid mTwoColumns"></div>
<div class="BasicGrid mTwoColumns mGapWide"></div>

这 HTML 代码难道不漂亮吗?它既易于阅读又易于理解,简直就像一件艺术品!

现在,CSS 的样式将会是这样的:

.BasicGrid {/* ... */}


/* 列修饰符 Column modifiers */
.BasicGrid.mOneColumn {/* ... */}
.BasicGrid.mTwoColumns {/* ... */}


/* 间隔修饰符 Gap modifiers */
.BasicGrid.mGapWide {/* ... */}

我认为这份 CSS 也非常容易理解。

此时,谈论驼峰 CSS 中的样式元素是很有必要的……我们很快就会讲到这一点,我保证!

在到达目的地之前,我们先来总结一下最后的一项好处。

好处 5:用驼峰 CSS 这种方法来给组件命名要容易得多

在 BEM 方法中进行命名是很困难的,因为我们必须发挥创造力,才能为各个模块找到单个单词的名称。这是因为使用双词名称会……让人难以忍受地难以阅读。

如果再进一步深入思考,您还会发现,在 CSS 中给任何元素命名也是件困难的事,原因同样如此——我们几乎对每一个可用的类都试图使用单个单词作为名称!

这就是为什么开发人员常常抱怨在 CSS 中命名工作十分困难的原因。

而这一点恰好进一步证实了我之前所提出的观点,即破折号在我们的潜意识中起着分隔的作用……

所以,一旦你改用 CamelCSS,你就会发现命名问题一下子就不复存在了——这是因为我们可以使用两个词甚至三个词组成的名称。

就在前几天,我构建了一个组件,使我能够测试容器查询样式。下面是它的样子:

不同尺寸的容器用于测试容器查询样式

如果你思考一下在 BEM 架构中如何给这个组件命名……我敢肯定,你很难想出一个合适的名称。

但如果你使用 CamelCSS,命名就会变得非常简单。我直接将其命名为 CQTest,然后就结束了。如果我想更详细一些,当需要与其他开发人员一起使用时,我总是可以将其称为 ContainerQueriesTest

因此,这证明了我的观点:使用 CamelCSS 命名要比传统烤串容易得多。

好处已经讲完了。我之前答应过你,我将讨论CamelCSS中的样式元素,所以让我们开始吧。

在驼峰 CSS 中设置元素样式

在设置元素样式时,只要有可能,我们就应使用直接后代选择器。

.Block > .element { /* ... */ }

这是个简单示例。在这种情况下,BlockAnotherBlock 都包含一个 content 元素。

<div class="Block">
  <div class="content">
    <div class="AnotherBlock">
      <div class="content">...</div>
    </div>
  </div>
</div>

虽然例子是编的,但这意思你肯定明白。

我们之所以想要使用直接后代选择器,有两个原因。

  1. 使用 .Block > .content 可以避免 .Block .content .content 错误地样式设置。
  2. 其他可能有同名元素的块,仍能拥有 content 使用权。

总结

我认为驼峰 CSS 写法更好。文中我所阐述的诸多优点包括:

  1. Block 就像框架里的组件
  2. 可读性更好
  3. 可扩展性极强
  4. 能合成
  5. 简单便捷

要是还想听我讲这个,可以推特留言,或者发邮件