CSS 实用程序类:您的可扩展样式库有哪些潜在的缺点?

2025-06-08

CSS 实用程序类:您的可扩展样式库

潜在的缺点是什么?

作者:Russell Bishop✏️

使用 CSS 实用类构建代码,可以极大地提升效率和组织性。它允许你定义一个property: value包含所有样式的库,并通过一个目录进行管理。

在本文中,我们将介绍:

  1. 什么是 CSS 实用程序类以及为什么要使用它们
  2. 流行框架中的快速演示
  3. 如何构建你自己的超级实用程序库

什么是实用程序类?

实用程序类是自描述的、单一用途的 CSS 类:

.flex {
  display: flex;
}
Enter fullscreen mode Exit fullscreen mode

开发人员使用这些功能类来构建而无需编写额外的 CSS,因为如果样式在库中,您可以反复使用它……

<aside class="flex flex-column bg-black">
  <div class="flex align-center justify-center">
    <img src="#" />
  </div>

  <div class="flex flex-column">
    <h1>Jamie Thrift</h1>
    <p class="flex align-center">
      <svg></svg>
      Head of HR
    </p>
  </div>
</aside>
Enter fullscreen mode Exit fullscreen mode

这些类准确地告诉我们它们的作用,因此开发人员可以直观地看到这些元素的布局方式,而不需要扫描底层 CSS。

CSS 实用程序类示例 GIF

实用程序类是如何组合在一起的?

实用程序类是作为框架的一部分为您生成的。其中一些流行的工具和框架提供了许多开箱即用的样式,因此您可以放心使用class="padding-10",因为样式已经存在。其他一些工具和框架则提供工具,让您仅定义项目所需的实用程序。

通过在一个地方配置样式库,您可以避免代码库中充斥着异常值,并且避免忽略哪些是新的 CSS 以及哪些已经在其他地方编写。

使用实用程序可以极大地增强项目的组织性,并提高开发人员和最终用户的可预测性和一致性。

LogRocket 免费试用横幅

就像一个设计系统!

这种思维模式在 UI 设计领域也得到了体现,如今,定义、复用和维护一个中央样式库的做法正在兴起。这些样式通常位于设计系统“样式指南”部分。

风格指南主要涵盖色彩、排版、间距、网格和图像等基础内容。这些底层基础是设计师为创作一致作品而遵循的一套规则。

样式指南组件示例

实用优先的 CSS 框架

多年来,许多框架、库和工具在这个领域逐渐流行起来。以下是一些知名选项的简要介绍,以及一些采用“实用优先”方法的开发者的案例研究。

2013

低音

  • 标语: “闪电般快速的模块化 CSS,无副作用”
  • GitHub: basscss/basscss

超光速粒子

2014

胡须

2015

炮塔

2017

Tailwind CSS

2018

stemCSS

Tailwind CSS 超光速粒子 iotaCSS
最佳功能 出色的文档竖起大拇指竖起大拇指 预组合组件,快速开发 组件对象实用程序直观组织
最差的特点 Java脚本配置 难以定制 每组实用程序都是一个包
类语法 .text-2xl 或者… .propertyalias-valuealias .f-headline 或者… .propertyalias-valuealias .o-type-small 或者... .namespace-propertyalias-valuealias
响应式语法 .md:flex-shrink-0 或者… .breakpointalias:propertyalias-valuealias .w-100-m 或者… .propertyalias-valuealias-breakpointalias u-text-right@sm 或者… .namespace-propertyalias-valuealias-breakpointalias
悬停/焦点语法 .focus:outline-none 或者… .state:propertyalias-valuealias .hover-orange 或者... 有限的组合.state-propertyalias-valuealias .u-color-orange@hover 或者… .namespace-propertyalias-valuealias-state
内置 Java脚本(配置 CSS CSS SCSS

将实用类付诸实践

让我们构建一个简单的组件来了解这些工具类是如何工作的。这里,我们将复制你可能在Stack Overflow上见过的 author 组件。

Stack Overflow 作者组件示例

我将使用Tailwind CSS中的语法作为此示例。

代码笔

<div class="p-3">
  <p class="text-gray-600">answered <span title="2017-08-07 08:46:14Z">Aug 7 '17 at 8:46</span></p>

  <figure class="flex items-center mt-3">
    <img class="w-10 h-10" src="https://assets-us-01.kc-usercontent.com/525f1d2d-c241-00a9-f169-8b2061aeb3da/e53a3a2b-f539-4ab9-a3ec-7cf7a2b6a664/aleksandra_zamojc.png" alt="Photo of Konrad Albrecht" />
    <figcaption class="ml-2">
      <p>
        <a class="text-blue-600" href="/user">Konrad Albrecht</a>
      </p>
      <p class="flex items-center mt-1">
        <span class="font-bold text-gray-800" title="reputation score">138</span>
        <span class="rounded-full w-2 h-2 bg-orange-400 ml-2"></span>
        <span class="ml-1 text-gray-600" title="badge count">10</span>
      </p>
    </figcaption>
  </figure>
</div>
Enter fullscreen mode Exit fullscreen mode

没什么可说的

“一旦你给它起了名字,你就会对它产生依恋。”

请注意,这些标记都不需要我们命名任何东西——因为命名本身就很难。我们不必遵循类似.user或 的结构.author……这使我们能够更快地构建组件,并减少维护工作。如果我们稍后添加另一个包含作者的组件,我们也不必重新考虑命名方案。

可预测性

说实话,在开始构建该组件之前,我只是快速浏览了Tailwind CSS 文档,并且只需要查找两个我无法猜到的类(.rounded-fullitems-center)。

这意味着构建组件可以在一个文件中进行,而不必在两个文件中切换。

组件翻转

潜在的缺点是什么?

正如您在开发中采用的任何方法一样,总会有一些妥协。让我们来探讨一下其中的一些复杂之处。

长类组合

一行无休止的、换行的文本很难辨认。在编写普通 CSS 时,我们有一些技巧可以帮助我们扫描一组属性:

  • 每行一条规则
  • 缩进
  • 规则维护属性排序顺序
  • 断点可以与预处理/后处理嵌套

当我们将那一长串样式重新放到标记中时,我们倾向于放弃上述做法,因为这会使 HTML 代码变得非常冗长。这会导致类名字符串变得非常长,难以理解,甚至难以进行 lint 检查。

尝试在这里找到您想要调整的重量属性:

<a href="https://myurl.com/" class="bgcolor-blue bg-color-darkblue@hover bg-color-darkblue@focus radius-tlbr-3 radius-trbl-12 font-europa color-light-grey color-white@hover color-white@focus weight-medium shadow-black-thin padv-3 padh-4 whitespace-nowrap size-6rem line-thin pos-relative top-3@hover top-3@focus">Incredible pace in this place</a>
Enter fullscreen mode Exit fullscreen mode

值得一提的是,这个例子甚至没有任何响应样式——如果它在一个或两个断点处改变属性,那么长度可能会加倍。

文件大小管理

如果我们全力以赴,有些框架会为每个断点每个状态每个属性每个可能值生成一个类。这会造成巨大的 CSS 垃圾,而你可能只使用了其中的 1%。框架知道这一点,并且有自己的处理方法。

Tailwind CSS(最小化后为 350kB)有一个关于控制文件大小的部分,建议设置PurgeCSS

Purge 会扫描你的项目文件,查找你使用过的类并从编译后的 CSS 文件中删除剩余的类。这意味着构建过程中会多出一个步骤,如果你的代码库相当庞大,甚至可能导致脚本运行延迟。此外,你的编程语言可能会掩盖类名(class="bgcolor-${foo}")的准确性,这需要你多加注​​意。

复杂选择器

CSS 的许多实用快捷键都包含在其强大的选择器中。例如,假设有一个表单部分,当用户不与其交互时,它会逐渐消失:

.fieldset:not(:focus-within):not(:hover) {
  opacity: .5;
}
Enter fullscreen mode Exit fullscreen mode

或者仅使用伪元素:

.title:after {
  position: absolute;
  bottom: -2px;
  left: 0;
  right: 0;
  border-bottom: 2px solid orange;
  content: '';
}
Enter fullscreen mode Exit fullscreen mode

或者甚至像斑马条纹表格行这样简单的东西:

.table-row:nth-child(odd) {
  background-color: grey;
}
Enter fullscreen mode Exit fullscreen mode

要使用(相当常见的!)此类样式,您可能最终需要在实用程序框架旁边维护一些自定义 CSS,或者使用iotaCSS 中的组件

失去级联

不仅如此,使用级联进行定位还可以在布局元素时节省大量时间 - 就像部署每个人都喜欢的猫头鹰选择器一样:

.margin-top-siblings-2 > * + * {
  margin-top: 2rem;
}
Enter fullscreen mode Exit fullscreen mode

我见过的任何框架都没有默认提供这些节省时间的样式,这意味着您最终需要在标记中添加更多的类来进行补偿。

构建您自己的实用程序框架

在提供了有关实用程序类的两个方面的故事之后,我们可以肯定地看到现有框架的提供方式还有改进的空间。

为了在 SCSS 中构建我们自己的实用程序框架,我们将遵循以下原则:

  • 只生成你需要的类
  • 减少编译 CSS 中的浪费
  • 造型类型的自然分离

为了实现这一目标,我们将利用:

  • %placeholders@extend
  • 命名空间对象组件.u-.o-.c-
  • 循环和列表

为什么要使用命名空间?

为您的类类型使用一个小的命名空间,您就可以通过一种结构化的方式根据类的类型来指示类的功能。我们之前提到的大多数框架都不会这样做,因为它们只是为了满足实用程序而编写的。

我们将在以下所有示例中使用命名空间。

定义样式的占位符

占位符是 SCSS 中用于定义样式的“幽灵”定义。它们的独特之处在于,除非你在某处引用它们,否则它们不会编译通过。就好像你定义了一个$variable但从未使用过它一样。

// Define the placeholder…

%u-display-flex {
  display: flex;
}

// This will not compile, until…

.u-flex {
  @extend %u-display-flex;
}

// Now we have our utility class ready to use…

.u-flex {
  display: flex;
}
Enter fullscreen mode Exit fullscreen mode

因为占位符只有在我们需要时才会出现,所以我们还可以继续为每个断点和样式的状态变化创建一个占位符。

@extend应用我们的占位符

定义好占位符后,我们现在可以决定如何使用它们了。通过使用@extend(而不是Tailwind CSS 的@apply),我们还可以减少 CSS 中的重复次数。

举个例子,假设我们的许多组件都使用特定的边框。如果我们的 CSS 代码如下,就会显得过大:

.c-sidebar {
  border: 1px solid #222;
}

.c-card {
  border: 1px solid #222;
}

.c-header {
  border: 1px solid #222;
}
Enter fullscreen mode Exit fullscreen mode

当我们持续使用低特异性类时,我们可以将这些样式组合在一起并消除最终结果:

.c-sidebar {
  @extend %border-grey;
}

.c-card {
  @extend %border-grey;
}

.c-header {
  @extend %border-grey;
}

// Which compiles to…

.c-sidebar, 
.c-card, 
.c-header {
  border: 1px solid #222;
}
Enter fullscreen mode Exit fullscreen mode

重复样式 vs. 使用@extend

我们的组件可以@extend使用库中现有的任何样式,通过引用占位符,我们知道不会创建浪费资源的新样式定义。让我们重写一下之前那个凌乱的组件示例:

.c-featured-link {
  @extend
  %u-pos-relative,
  %u-padv-3,
  %u-padh-4,
  %u-bgcolor-blue,
  %u-shadow-black-thin,
  %u-radius-tlbr-3,
  %u-radius-trbl-12,
  %u-weight-medium,
  %u-size-6rem,
  %u-line-thin,
  %u-font-europa,
  %u-color-light-grey,
  %u-whitespace-nowrap;
}
Enter fullscreen mode Exit fullscreen mode

嵌套在组件内

我们还可以通过嵌套断点和状态来大大提高这些组件的可读性。

.c-featured-link {
  // …

  &:hover,
  &:focus {
    @extend 
    %u-top-3,
    %u-bg-color-darkblue,
    %u-color-white;
  }
}
Enter fullscreen mode Exit fullscreen mode

构建我们需要的工具

最后,我将向您介绍在 SCSS 中实现上述功能所需的工具。我将首先描述它们的工作原理,以便您可以根据自己的喜好重新构建它们,然后提供一个已经包含完整工具集的框架链接。

制作占位符

@mixin make-placeholder($utility-name, $properties) {

  // No breakpoint
  %#{$utility-name} {
    @each $property, $value in $properties {
      #{$property}: $value;
    }
  }

  // Every breakpoint
  @each $breakpoint-key, $breakpoint-value in $global-breakpoints {
    %#{$utility-name + $global-breakpoint-separator + $breakpoint-key} {
      @include breakpoint($breakpoint-key) {
        @each $property, $value in $properties {
          #{$property}: $value;
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

上面的 mixin 首先为$properties参数中发送的每个属性创建一个占位符。然后,我们循环遍历 map 中其他地方定义的所有断点,$global-breakpoints以确保我们也拥有断点占位符。

这将为我们提供如下占位符:

@media (min-width: 600px) {
  %u-bgcolor-primary\@medium {
    background-color: blue;
  }
}
Enter fullscreen mode Exit fullscreen mode

我们转义 @ 符号(我们的断点和状态标识符),因为它是一个无效字符;欢迎您选择自己的分隔符来替换它。

从占位符创建类

@mixin make-class-from-placeholder($utility-name, $properties) {
  .#{$utility-name} {
    @extend %#{$utility-name};
  }
}
Enter fullscreen mode Exit fullscreen mode

由于我们需要(在下一个 mixin 中),直接从我们的 new 创建一个实用程序类%placeholder

制作实用程序

@mixin make-utility($args) {

  $class: map-use(
    $args,
    class,
    false
  );

  $args: map-remove($args, class);

  // If 'alias' key exists in $args, use that for the placeholder-name,
  // otherwise use the key and value of the first property in $args
  $utility-name: map-use(
    $args,
    alias,
    first(map-keys($args)) + '-' + first(map-values($args))
  );

  $utility-name: 'u-' + $utility-name;

  $properties: map-remove($args, alias);

  @include make-placeholder($utility-name, $properties);

  @if ($class) {
    @include make-class-from-placeholder($utility-name, $properties);
  }

}
Enter fullscreen mode Exit fullscreen mode

最后一步是支持别名,这使您可以决定是否喜欢在类名中逐字母命名(justify-content-space-between)或者是否喜欢使用小快捷方式(jc-sb)。

这完全取决于您 - 较长的名称意味着任何开发人员都可以加入并立即了解模式,但较短的名称更容易扫描并且输入速度更快。

确定别名(或留空)后,我们会检查您是否要使用 Make Class 并完成占位符。

用循环和控制指令把它们组合在一起

为了充分利用我们的占位符构建,您需要列出要添加到库中的属性和值。

这里有一些有用的循环可以帮助您:

@each $wrap in (nowrap, wrap, wrap-reverse) { 
  @include make-utility((
    alias: 'fw-' + $wrap,
    flex-wrap: $wrap
  ));
}


@each $property in (translateX, translateY) {
  @each $value in (-100, -50, 0, 50, 100) {
    @include make-utility((
      alias: $property + '-' + $value + $global-unit-percent,
      transform: $property + '(' + $value + '%)'
    ));
  }
}


@each $property in (margin-top, margin-right, margin-bottom, margin-left) {
  @for $value from 1 through 30 {
    @include make-utility((
      $property: #{$value + 'rem'};
    ));
  }
}
Enter fullscreen mode Exit fullscreen mode

如果您想快速启动自己的框架,可以获取最新版本的stemCSS

概括

近期的工具和框架将实用类的概念推向了主流,并且它们也从其出色的文档中获益良多(感谢Tailwind CSS)。显然,实用类已经彻底改变了开发者使用低优先级 CSS 类作为一致性样式方法的格局。

样式表的未来将会给我们带来什么?


编者注:觉得这篇文章有什么问题?您可以在这里找到正确版本

插件:LogRocket,一个用于 Web 应用的 DVR

 
LogRocket 仪表板免费试用横幅
 
LogRocket是一款前端日志工具,可让您重播问题,就像它们发生在您自己的浏览器中一样。您无需猜测错误发生的原因,也无需要求用户提供屏幕截图和日志转储,LogRocket 允许您重播会话以快速了解问题所在。它可与任何应用程序完美兼容,无论使用哪种框架,并且提供插件来记录来自 Redux、Vuex 和 @ngrx/store 的更多上下文。
 
除了记录 Redux 操作和状态之外,LogRocket 还记录控制台日志、JavaScript 错误、堆栈跟踪、带有标头 + 正文的网络请求/响应、浏览器元数据以及自定义日志。它还会对 DOM 进行插桩,以记录页面上的 HTML 和 CSS,即使是最复杂的单页应用程序,也能重现像素完美的视频。
 
免费试用


CSS 实用程序类:您的可扩展样式库首先出现在LogRocket 博客上。

鏂囩珷鏉ユ簮锛�https://dev.to/bnevilleoneill/css-utility-classes-your-library-of-extendable-styles-4h3c
PREV
从 create-react-app 到 PWA
NEXT
Building a sentiment analysis app with Node.js