1. 核心概念
  2. 重用样式

Tailwind 鼓励 utility-first 工作流程,其中设计仅使用底层工具类来实现。这是避免过早抽象和随之而来的痛点的有效方法。

¥Tailwind encourages a utility-first workflow, where designs are implemented using only low-level utility classes. This is a powerful way to avoid premature abstraction and the pain points that come with it.

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

¥But of course as a project grows, you’ll inevitably find yourself repeating common utility combinations to recreate the same design in many different places.

例如,在下面的模板中,你可以看到每个头像图片的工具类重复了五次:

¥For example, in the template below you can see the utility classes for each avatar image are repeated five separate times:

Contributors

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>

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

¥Don’t panic! In this guide, you’ll learn about different strategies for reusing styles in your project, as well as best practices for when to employ each one.


使用编辑器和语言功能

¥Using editor and language features

很多时候,像这样的重复甚至都不是真正的问题,因为它都在一个地方,或者甚至根本不存在,因为你正在迭代一组项目并且只编写一次标记。

¥A lot of the time, duplication like this isn’t even a real problem because it’s all together in one place, or doesn’t even actually exist because you’re iterating over an array of items and only writing the markup once.

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

¥If the styles you need to reuse only need to be reused within a single file, multi-cursor editing and loops are the simplest way to manage any duplication.

多光标编辑

¥Multi-cursor editing

当重复定位到单个文件中的一组元素时,最简单的处理方法是使用 多光标编辑 一次快速选择和编辑每个元素的类列表:

¥When duplication is localized to a group of elements in a single file, the easiest way to deal with it to use multi-cursor editing to quickly select and edit the class list for each element at once:

<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>

你会惊讶于这最终成为最佳解决方案的频率。如果你可以同时快速编辑所有重复的类列表,那么引入任何额外的抽象就没有任何好处。

¥You’d be surprised at how often this ends up being the best solution. If you can quickly edit all of the duplicated class lists simultaneously, there’s no benefit to introducing any additional abstraction.

循环

¥Loops

在你假设你将需要提取一个组件或为某物创建一个自定义类之前,请确保你实际上在你的模板中多次使用它。

¥Before you assume you’re going to need to extract a component or create a custom class for something, make sure you’re actually using it more than once in your template.

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

¥A lot of the time a design element that shows up more than once in the rendered page is only actually authored once because the actual markup is rendered in a loop.

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

¥For example, the duplicate avatars at the beginning of this guide would almost certainly be rendered in a loop in a real project:

Contributors

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 重写导航示例:

¥You could even rewrite the navigation example using a loop or map if you preferred as well:

<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>

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

¥When elements are rendered in a loop like this, the actual class list is only written once so there’s no actual duplication problem to solve.


提取组件和部分

¥Extracting components and partials

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

¥If you need to reuse some styles across multiple files, the best strategy is to create a component if you’re using a front-end framework like React, Svelte, or Vue, or a template partial if you’re using a templating language like Blade, ERB, Twig, or Nunjucks.

Beach
Private Villa
$299 USD per night
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>

现在,你可以在任意多个地方使用该组件,同时仍然拥有样式的单一真实来源,因此可以轻松地在一个地方一起更新它们。

¥Now you can use this component in as many places as you like, while still having a single source of truth for the styles so they can easily be updated together in one place.

与 CSS 抽象相比

¥Compared to CSS abstractions

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

¥Unless a component is a single HTML element, the information needed to define it can’t be captured in CSS alone. For anything even remotely complex, the HTML structure is just as important as the CSS.

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

ChitChat

You have a new message!

<!-- 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">
    <div class="chat-notification-title">ChitChat</div>
    <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。当然,你可以在一个地方为每个实例更新字体大小,但是如果你需要将标题变成链接怎么办?

¥Even if you create classes for the different elements in a component like this, you still have to duplicate the HTML every time you want to use this component. Sure you can update the font-size for every instance in a single place, but what if you need to turn the title into a link?

组件和模板部分比纯 CSS 抽象更好地解决了这个问题,因为组件可以封装 HTML 和样式。更改每个实例的字体大小与使用 CSS 一样简单,但现在你也可以在一个地方将所有标题转换为链接。

¥Components and template partials solve this problem much better than CSS-only abstractions because a component can encapsulate the HTML and the styles. Changing the font-size for every instance is just as easy as it is with CSS, but now you can turn all of the titles into links in a single place too.

创建模板部分或 JavaScript 组件

ChitChat

You have a new message!

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>
  )
}

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

¥When you create components and template partials like this, there’s no reason to use anything other than utility classes because you already have a single source of truth for the styles.


使用 @apply 提取类

¥Extracting classes with @apply

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

¥If you’re using a traditional templating language like ERB or Twig, creating a template partial for something as small as a button can feel like overkill compared to a simple CSS class like btn.

虽然强烈建议你为更复杂的组件创建适当的模板部分,但你可以使用 Tailwind 的 @apply 指令在模板部分感觉笨重时将重复的工具模式提取到自定义 CSS 类。

¥While it’s highly recommended that you create proper template partials for more complex components, you can use Tailwind’s @apply directive to extract repeated utility patterns to custom CSS classes when a template partial feels heavy-handed.

下面是一个 btn-primary 类可能看起来像使用 @apply 从现有工具组合它:

¥Here’s what a btn-primary class might look like using @apply to compose it from existing utilities:

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 的更多信息。

¥Learn more about @apply and @layer in the Functions & Directives documentation.

避免过早抽象

¥Avoiding premature abstraction

无论你做什么,不要只是为了让事情看起来像 “cleaner” 而使用 @apply。是的,散落着 Tailwind 类的 HTML 模板有点难看。在具有大量自定义 CSS 的项目中进行更改更糟糕。

¥Whatever you do, don’t use @apply just to make things look “cleaner”. Yes, HTML templates littered with Tailwind classes are kind of ugly. Making changes in a project that has tons of custom CSS is worse.

如果你开始对所有内容都使用 @apply,那么你基本上只是再次编写 CSS,并放弃了 Tailwind 为你提供的所有工作流程和可维护性优势,例如:

¥If you start using @apply for everything, you are basically just writing CSS again and throwing away all of the workflow and maintainability advantages Tailwind gives you, for example:

  • 你必须一直想出类名 - 没有什么比为不值得命名的东西想出一个类名更能让你放慢速度或耗尽你的精力了。

    ¥You have to think up class names all the time — nothing will slow you down or drain your energy like coming up with a class name for something that doesn’t deserve to be named.

  • 你必须在多个文件之间跳转才能进行更改 - 这是一个比你在将所有内容共置在一起之前想象的更大的工作流程杀手。

    ¥You have to jump between multiple files to make changes — which is a way bigger workflow killer than you’d think before co-locating everything together.

  • 更改样式更可怕 - CSS 是全局的,你确定可以更改该类中的 min-width 值而不破坏网站其他部分的某些内容吗?

    ¥Changing styles is scarier — CSS is global, are you sure you can change the min-width value in that class without breaking something in another part of the site?

  • 你的 CSS 包会更大 - 噢。

    ¥Your CSS bundle will be bigger — oof.

如果你打算使用 @apply,请将其用于非常小、高度可重用的事物,例如按钮和表单控件 - 即使如此,前提是你不使用像 React 这样的框架,其中组件是更好的选择。

¥If you’re going to use @apply, use it for very small, highly reusable things like buttons and form controls — and even then only if you’re not using a framework like React where a component would be a better choice.