原文信息
- 文章地址:How camel-cased classes are way better than kebab-cased classes
- 发布时间:2023/02/15
声明
代码 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 现在看着普通,实则不然。
这其中有:
- block 就像框架的组件
- 可读性更好
- 可扩展性更好
- 能组合类名
- 组件命名更容易
一起探讨这五项益处。
好处 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 中可以分别对 Block 和 mModifier 进行设置。
以下是 CSS 应该采用的正确写法。
.Block { } /* 块 */
.Block.mModifier { } /* 修饰符 */
写 CSS 时将 mModifier 类附加到到 .Block 上是很有必要的,原因有二:
- 单独使用修饰符没意义,必须和修饰的内容一起用。将
mModifier附加给Block,等于为Modifier名称开辟了使用空间,使其能够应用于其他块。 - 这种更高的权重确保了
.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 { /* ... */ }
这是个简单示例。在这种情况下,Block 和 AnotherBlock 都包含一个 content 元素。
<div class="Block">
<div class="content">
<div class="AnotherBlock">
<div class="content">...</div>
</div>
</div>
</div>
虽然例子是编的,但这意思你肯定明白。
我们之所以想要使用直接后代选择器,有两个原因。
- 使用
.Block > .content可以避免.Block .content .content错误地样式设置。 - 其他可能有同名元素的块,仍能拥有
content使用权。
总结
我认为驼峰 CSS 写法更好。文中我所阐述的诸多优点包括:
- Block 就像框架里的组件
- 可读性更好
- 可扩展性极强
- 能合成
- 简单便捷
要是还想听我讲这个,可以推特留言,或者发邮件!