Tailwind 鼓励使用 实用优先 工作流程,其中设计仅使用低级实用程序类实现。这是一种强大的方法,可以避免过早抽象及其带来的痛点。

但当然,随着项目的增长,您不可避免地会发现自己重复使用常见的实用程序组合来在许多不同的地方重新创建相同的设计。

例如,在下面的模板中,您可以看到每个头像图像的实用程序类重复了五次。

贡献者

204
<div>
  <div class="flex items-center space-x-2 text-base">
    <h4 class="font-semibold text-slate-900">Contributors</h4>
    <span class="rounded-full bg-slate-100 px-2 py-1 text-xs font-semibold text-slate-700">204</span>
  </div>
  <div class="mt-3 flex -space-x-2 overflow-hidden">
    <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1491528323818-fdd1faba62cc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt=""/>
    <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1550525811-e5869dd03032?ixlib=rb-1.2.1&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt=""/>
    <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1500648767791-00dcc994a43e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2.25&w=256&h=256&q=80" alt=""/>
    <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt=""/>
    <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1517365830460-955ce3ccd263?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt=""/>
  </div>
  <div class="mt-3 text-sm font-medium">
    <a href="#" class="text-blue-500">+ 198 others</a>
  </div>
</div>

别担心!在本指南中,您将了解在项目中重用样式的不同策略,以及何时使用每种策略的最佳实践。


使用编辑器和语言功能

很多时候,这种重复甚至不是一个真正的问题,因为它们都集中在一个地方,或者甚至不存在,因为您正在遍历一个项目数组,并且只编写一次标记。

如果您需要重用的样式只需要在单个文件中重用,则多光标编辑和循环是管理任何重复的最简单方法。

多光标编辑

当重复局限于单个文件中的元素组时,处理它的最简单方法是使用 多光标编辑 来快速选择并同时编辑每个元素的类列表。

<nav class="flex justify-center space-x-4">
  <a href="/dashboard" class="font-medium px-3 py-2 text-slate-700 rounded-lg hover:bg-slate-100 hover:text-slate-900">Home</a>
  <a href="/team" class="font-medium px-3 py-2 text-slate-700 rounded-lg hover:bg-slate-100 hover:text-slate-900">Team</a>
  <a href="/projects" class="font-medium px-3 py-2 text-slate-700 rounded-lg hover:bg-slate-100 hover:text-slate-900">Projects</a>
  <a href="/reports" class="font-medium px-3 py-2 text-slate-700 rounded-lg hover:bg-slate-100 hover:text-slate-900">Reports</a>
</nav>

您会惊讶地发现,这往往是最好的解决方案。如果您能够快速地同时编辑所有重复的类列表,那么引入任何额外的抽象就没有意义。

循环

在你认为需要提取组件或创建自定义类之前,请确保你确实在模板中多次使用它。

很多时候,在渲染页面中多次出现的页面元素实际上只编写了一次,因为实际的标记是在循环中渲染的。

例如,本指南开头处的重复头像几乎肯定会在实际项目中循环渲染。

贡献者

204
<div>
  <div class="flex items-center space-x-2 text-base">
    <h4 class="font-semibold text-slate-900">Contributors</h4>
    <span class="rounded-full bg-slate-100 px-2 py-1 text-xs font-semibold text-slate-700">204</span>
  </div>
  <div class="mt-3 flex -space-x-2 overflow-hidden">
    {#each contributors as user}
      <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="{user.avatarUrl}" alt="{user.handle}"/>
    {/each}
  </div>
  <div class="mt-3 text-sm font-medium">
    <a href="#" class="text-blue-500">+ 198 others</a>
  </div>
</div>

你甚至可以根据自己的喜好使用循环或map重写导航示例。

<nav className="flex sm:justify-center space-x-4">
  {[
    ['Home', '/dashboard'],
    ['Team', '/team'],
    ['Projects', '/projects'],
    ['Reports', '/reports'],
  ].map(([title, url]) => (
    <a href={url} className="rounded-lg px-3 py-2 text-slate-700 font-medium hover:bg-slate-100 hover:text-slate-900">{title}</a>
  ))}
</nav>

当元素像这样在循环中渲染时,实际的类列表只编写一次,因此没有实际的重复问题需要解决。


提取组件和部分

如果你需要在多个文件中重用一些样式,最好的策略是创建一个组件(如果你使用的是 React、Svelte 或 Vue 等前端框架),或者创建一个模板部分(如果你使用的是 Blade、ERB、Twig 或 Nunjucks 等模板语言)。

Beach
私人别墅
每晚 299 美元
VacationCard.vue
<template>
  <div>
    <img class="rounded" :src="img" :alt="imgAlt">
    <div class="mt-2">
      <div>
        <div class="text-xs text-slate-600 uppercase font-bold tracking-wider">{{ eyebrow }}</div>
        <div class="font-bold text-slate-700 leading-snug">
          <a :href="url" class="hover:underline">{{ title }}</a>
        </div>
        <div class="mt-2 text-sm text-slate-600">{{ pricing }}</div>
      </div>
    </div>
  </div>
</template>

<script>
  export default {
    props: ['img', 'imgAlt', 'eyebrow', 'title', 'pricing', 'url']
  }
</script>

现在你可以根据需要在任何地方使用这个组件,同时仍然拥有样式的单一事实来源,以便它们可以轻松地在同一个地方一起更新。

与 CSS 抽象相比

除非组件是单个 HTML 元素,否则定义它所需的信息无法仅通过 CSS 捕获。对于任何稍微复杂的东西,HTML 结构与 CSS 一样重要。

不要依赖 CSS 类来提取复杂组件

ChitChat

您有一条新消息!

<!-- Even with custom CSS, you still need to duplicate this HTML structure -->
<div class="chat-notification">
  <div class="chat-notification-logo-wrapper">
    <img class="chat-notification-logo" src="/img/logo.svg" alt="ChitChat Logo">
  </div>
  <div class="chat-notification-content">
    <h4 class="chat-notification-title">ChitChat</h4>
    <p class="chat-notification-message">You have a new message!</p>
  </div>
</div>

<style>
  .chat-notification { /* ... */ }
  .chat-notification-logo-wrapper { /* ... */ }
  .chat-notification-logo { /* ... */ }
  .chat-notification-content { /* ... */ }
  .chat-notification-title { /* ... */ }
  .chat-notification-message { /* ... */ }
</style>

即使您为组件中的不同元素创建了像这样的类,您仍然需要在每次使用此组件时复制 HTML。当然,您可以更新所有实例的字体大小,但如果需要将标题变成链接怎么办?

组件和模板部分比仅 CSS 抽象更好地解决了这个问题,因为组件可以封装 HTML 样式。更改所有实例的字体大小与 CSS 一样容易,但现在您也可以在一个地方将所有标题变成链接。

创建模板部分或 JavaScript 组件

ChitChat

您有一条新消息!

Notification.jsx
function Notification({ imageUrl, imageAlt, title, message }) {
  return (
    <div className="p-6 max-w-sm mx-auto bg-white rounded-xl shadow-md flex items-center space-x-4">
      <div className="shrink-0">
        <img className="h-12 w-12" src={imageUrl.src} alt={imageAlt}>
      </div>
      <div>
        <div className="text-xl font-medium text-black">{title}</div>
        <p className="text-slate-500">{message}</p>
      </div>
    </div>
  )
}

当您创建像这样的组件和模板部分时,没有理由使用除实用程序类以外的任何东西,因为您已经拥有样式的单一事实来源。


使用 @apply 提取类

如果您使用的是像 ERB 或 Twig 这样的传统模板语言,那么为像按钮这样的小东西创建模板部分,与简单的 CSS 类 btn 相比,感觉有点过分。

虽然强烈建议您为更复杂的组件创建合适的模板部分,但当模板部分感觉过于繁重时,您可以使用 Tailwind 的 @apply 指令将重复的实用程序模式提取到自定义 CSS 类中。

以下是使用@apply从现有实用程序组合btn-primary类的示例

HTML
<!-- Before extracting a custom class -->
<button class="py-2 px-5 bg-violet-500 text-white font-semibold rounded-full shadow-md hover:bg-violet-700 focus:outline-none focus:ring focus:ring-violet-400 focus:ring-opacity-75">
  Save changes
</button>

<!-- After extracting a custom class -->
<button class="btn-primary">
  Save changes
</button>
CSS
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer components {
  .btn-primary {
    @apply py-2 px-5 bg-violet-500 text-white font-semibold rounded-full shadow-md hover:bg-violet-700 focus:outline-none focus:ring focus:ring-violet-400 focus:ring-opacity-75;
  }
}

函数和指令文档中了解更多关于@apply@layer的信息。

避免过早抽象

无论你做什么,不要仅仅为了让代码看起来“更干净”而使用@apply。是的,到处都是 Tailwind 类的 HTML 模板确实有点难看。但在一个有大量自定义 CSS 的项目中进行更改更糟糕。

如果你开始对所有东西都使用@apply,你基本上就是在重新编写 CSS,并且抛弃了 Tailwind 带来的所有工作流程和可维护性优势,例如

  • 你必须一直想出类名——没有什么比为不值得命名的东西想出类名更能拖慢你的速度或消耗你的精力了。
  • 你必须在多个文件之间跳转才能进行更改——这比你想象的要更能影响工作流程,因为你必须将所有东西都放在一起。
  • 更改样式更可怕——CSS 是全局的,你确定你可以更改该类中的最小宽度值而不会破坏网站的其他部分吗?
  • 你的 CSS 包会更大——糟糕。

如果你要使用@apply,请将其用于非常小、高度可重用的东西,比如按钮和表单控件——即使这样,也只有在你没有使用 React 这样的框架时才使用,因为在这种情况下,组件会是更好的选择。