以下是 Jon Yablonski 的客座文章。Jon 将向我们展示一个如何构建标记的示例,以便一个组件特别通用。它按原样工作,并具有标准化的方法来进行变体(添加单个类),从而允许设计更改以适应情况。

从像素完美的 Photoshop 文件构建固定宽度网页的日子已经一去不复返了。为了生成能够智能适应任何屏幕尺寸的灵活布局,我们的工作流程发生了变化,变得更加迭代和敏捷。我们已经了解了模块化的重要性,以及它如何促进我们保持浏览器灵活所需的灵活性。作为一名设计师和前端开发人员,这种灵活性是我处理 Web 项目的方式中不可或缺的一部分。我发现自己更多地是在浏览器中做出设计决策而不是设计文件,为了支持此工作流程,我需要能够构建易于扩展的用户界面模块。
可扩展模块
我非常重视的是可扩展性,这是一种系统设计原则,其中实现考虑了未来的增长。可扩展性的核心主题是在最大程度地减少对现有系统的影响的同时,为变化提供支持。对于前端开发,这意味着构建可以轻松扩展以适应用户界面需求的模块变体,同时保留模块的基础。最终结果是减少代码膨胀,减少由于一次性解决方案导致的代码库碎片化,以及更易于维护的代码。
构建可扩展性
为了确保组件和模式结构的一致性,有必要以可重复且可预测的方式构建它们。以下是我的基本步骤
步骤 1:确定模块类型
第一步是确定我们正在使用的模块类型,它可以分为两类:组件或模式。组件是一个独立的、模块化的对象,没有子元素,但可以具有更改其外观的修饰符状态(例如:按钮、消息、缩略图)。另一方面,模式是一个具有子元素(也可以是独立组件)的对象,所有这些子元素都受父对象的影响(例如:页眉、页眉徽标、页眉导航)。组件和模式都可以具有更改其外观或结构的修饰符状态。
步骤 2:定义模块基础
下一步是找到组件或模式的基础规则,组件的所有变体都将继承这些规则。这些规则应该相对较少,并且保留用于很少更改的属性。大多数情况下,我发现这些规则是诸如边距、填充、位置和显示之类的属性。
本文中的所有代码示例都使用 BEM(块、元素、修饰符)命名方法。BEM 提供了许多优点,但也许我最喜欢的一点是,我只需查看标记片段即可了解其功能。如果您想详细了解此命名方法,我建议您查看 本文 以获取良好的介绍。
HTML 基础
<div class="block"></div>
CSS 基础
.block {
position: relative;
margin: 0 0 1em;
}
步骤 3:定义常用元素
如果您正在构建组件,则可以跳过此步骤并直接转到下一步;但是,如果您正在构建包含子元素的模式,则下一步是定义常用元素。这些元素在主题上与父块相关(但可以作为独立组件存在于模式之外)。
HTML 常用元素
<div class="block">
<div class="block__element">…</div>
<div class="block__another-element">…</div>
</div>
CSS
.block__element {
padding: 1em;
background: white;
border: 1px solid black;
}
.block__another-element {
position: absolute;
top: 0;
right: 0;
}
步骤 4:使用修饰符扩展
最后一步是使用修饰符扩展您的组件/模式。修饰符本质上是扩展基础块和子元素的变体,并且可以根据需要创建。
HTML 修饰符类的示例
<div class="block block—modifier">
<div class="block__element">…</div>
<div class="block__another-element">…</div>
</div>
CSS 修改的示例
.block—modifier {
border-top: 3px solid red;
}
.block—modifier .block__element {
background: grey;
}
示例
现在我们已经了解了构建可扩展组件和模式所涉及的基本步骤,是时候探索一些示例了。我们将从一个相对简单的组件及其如何扩展以涵盖各种场景开始,然后我们将查看一个稍微复杂一些的模式。
关于组件
以下是几个常用组件及其变体的演示。每个组件都包含一个父块和扩展块样式的修饰符。这允许快速创建变体,使您能够灵活地快速迭代和调整组件以适应用户界面中的任何情况。
通用组件示例
查看 CodePen 上 Jon Yablonski (@jonyablonski) 编写的 可扩展通用组件。
组件本质上应该相对简单,因为它们不包含子元素。现在,让我们来看一些稍微复杂一点的东西。
关于模式
媒体模式是一个对象,它包含一个媒体元素(可以是图像或视频)和相关内容(通常以文本形式呈现)。您可能熟悉媒体模式的一种变体,称为“媒体对象”或“标记对象”,我们稍后会谈到。这种模式很好地说明了在设计时考虑到可扩展性如何提供无限的灵活性。
媒体默认模式
我们将从我们的默认模式开始,或者是在没有任何修饰符的情况下如何显示它。我在这里做了一些假设,利用了一个<article>
标签来提供更多语义信息,但这可以更改为任何你想要的标签。我们媒体模式的核心样式是
- 一个其子元素不换行的弹性容器
- 一点边距
这些是所有媒体模式将继承的样式。此外,每个媒体模式都将包含媒体项目(在本例中为图像),以及包含标题和定义列表的媒体主体。
查看 CodePen 上 Jon Yablonski (@jonyablonski) 编写的 默认媒体模式。
媒体卡片模式
虽然我们的媒体模式的这种默认变体在某些情况下可能足够,但在许多其他情况下,您可能需要彻底改变其外观。下一步是开始识别变体,这些变体将使我们的模式能够适应各种情况。让我们从一个与默认外观没有太大差异的变体开始——卡片变体。前提是我们的标记几乎不需要更改,并且我们可以通过简单地向父块添加一个修饰符类来更改模式的外观
查看 CodePen 上 Jon Yablonski (@jonyablonski) 编写的 媒体卡片模式。
媒体对象模式
假设以后,我发现我需要一个模式,当有足够的空间时,图像和文本并排显示。这是一种通常称为“媒体对象”的模式。为了创建它,我们可以简单地扩展我们已经拥有的媒体模式,以最大限度地减少冗余代码。
查看 CodePen 上 Jon Yablonski (@jonyablonski) 编写的 媒体对象模式。
媒体板条模式
让我们用一个真正考验它的变体来更进一步。我们定义的变体到目前为止几乎满足了我所有的设计需求,但我还需要一个更具影响力的变体。让我们创建一个变体,它跨越窗口的整个宽度,主体占据一半,图像占据另一半。此外,我还想确保主体内容与页面上的其他内容对齐。我们将这种变体称为“媒体板条”
查看 CodePen 上 Jon Yablonski (@jonyablonski) 编写的 媒体板条模式。
现在我们有几种媒体模式的变体:我们有默认变体、卡片变体、对象变体,最后是板条变体。这些模式的变体在不同的情况下都有用,并且它们都确保了相同的代码基础!这样做的好处是,对模式的任何更改都将影响所有模式,因此该模式的每个实例都将保持同步和一致。
结论
我们已经介绍了为什么在构建以灵活性和可维护性为中心的界面时,可扩展的组件和模式更好。为了说明这一点,我们介绍了创建一些可扩展组件所涉及的步骤。以这种方式构建界面的优势将立即变得显而易见,因为您将花费更少的时间进行重构以应对意外的设计更改或添加,并且构成这些组件的样式将更容易维护。
英雄所见略同!
我目前正在开发一个 CSS 框架,它使用了与您所写相同的理念。
太棒了!听起来非常有趣。
使用双下划线作为类名的想法是什么?
您可以阅读这篇关于 BEM 命名方法的精彩文章。
不错。太棒了……!!!
嘿,好文章,有时候感觉对于“简单”的事情来说这有点过头了,但养成好习惯是值得的。我还在理解 BEM。
模块化 CSS 的想法让我对 CSS 的未来感到兴奋。组件和模式是优秀设计的特点之一。谢谢,这是一篇很有启发性的文章。
非常欢迎你,Omeiza!
如果我们有元素/容器查询,我们可以将这种方法应用得更广泛。
我完全同意,Rhys
我正在使用这种方法 http://rscss.io
看起来是相同的方法,对吧?
感谢你的文章,Jon!我也认为网络都是关于组件的。在我看来,你误用了 BEM。你使用了像
media__body-list
和media__body-list-title
这样的类,我认为它们不属于media
块。将其拆分成块会更好吗?我将尝试使用您代码的简化摘录来演示它可以变成
因此,媒体信息是一个独立的实体。而且,我认为这个列表与
media
无关。我个人总是会有这样的疑问。你对此有什么看法?
我和你一样,你的最后一个例子看起来更简洁,也更容易扩展。太多人试图在选择器中复制实际的 DOM 结构。
Evan 的观点很有意思,但我认为“误用”这个词不太准确。这实际上取决于什么最适合你。就我个人而言,我更喜欢明确地看到子元素与父块的关系。块和元素之间的关系很清晰(‘block__element’)。这是使用 BEM(或其语法修改版)等命名方法的优势之一。你提供的示例让人感觉‘media-info’是一个父元素,并且可以独立存在,这具有误导性。我不介意“整体”这个说法,因为这种命名方式使块、元素和修饰符之间的关系非常清晰。
我向 Ivan 道歉。
你好,Jon!感谢你的解释!如果我的话语显得粗鲁或冒犯,我表示歉意。“误用”这个词在这里肯定是不合适的。
我的意思是,正如 Marvin 也提到的,一些开发者只是在
__
后添加他们需要的任何内容。因此,你可能会偶尔遇到这样的类名 :D在我看来,如果某些东西看起来像一个独立的实体,它就应该被拆分。
不用担心,Ivan!
我明白你的意思,但需要注意的是,块元素可以包含其他块元素。(例如:‘media__item item’,‘item’ 也是一个独立的块)。
命名方法的目的是清楚地展示父元素和子元素之间的关系,我发现 BEM 在这方面非常有用。再次强调,这取决于你自己的选择。
文章写得非常好。
我非常喜欢 BEM,因为它易于阅读,并且非常适合多个开发者协作。
我们使用两个连字符作为修饰符,以避免与使用连字符的元素混淆。
尽早掌握基础知识可以帮助开发者、项目经理,最终帮助客户。
谢谢,Neil!
不错的文章!让我想起了这本书:https://smacss.com/
一些原则与你在这里写的内容非常相似。对于任何对该主题感兴趣的人来说都很有趣。由于我不是 BEM 表示法的忠实粉丝,我更喜欢按照本书的方式来布局(即使与 BEM 类似;我只是不喜欢双下划线/连字符……这只是个人喜好)。然后,我想这都取决于我们对自己有多严格。
尽管如此,这是一篇有趣的文章!谢谢!
非常好的文章,还有很棒的示例。我喜欢它的可重用性。
这可能是我网站上未来视频的一个好主题。Jon,干得好。
为什么块-修饰符使用长连字符?我认为这是一个排版错误。修饰符命名应该使用两个减号,对吗?
您好,Chernenko。是的,修饰符类使用两个连字符。文章中的所有示例都遵循此模式。