
核心概念
管理重复和创建可重用的抽象。
Tailwind 鼓励使用实用优先的工作流程,即通过仅使用低级实用类来实现设计。这是一种强大的方式,可以避免过早抽象化以及随之而来的各种痛点。
🌐 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:
<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.
很多时候,这种重复其实并不是真正的问题,因为它都集中在一个地方,或者根本不存在,因为你只是遍历一个项目数组,并且只写了一次标记。
🌐 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.
当重复仅局限于单个文件中的一组元素时,处理它的最简单方法是使用多光标编辑来一次性快速选择并编辑每个元素的类列表:
🌐 When duplication is localized to a group of elements in a single file, the easiest way to deal with it is 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.
在你认为需要提取一个组件或为某个东西创建自定义类之前,先确保你在模板中确实多次使用了它。
🌐 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:
<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.
如果你需要在多个文件中重用一些样式,最好的策略是:如果你使用的是像 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.
<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.
除非一个组件只是单个 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 类来提取复杂组件
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 组件
You have a new message!
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.
如果你使用的是像 ERB 或 Twig 这样的传统模板语言,为像按钮这样的小东西创建一个模板片段,相比使用简单的 CSS 类如 btn,可能会显得有些大材小用。
🌐 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.
下面是一个使用 @apply 从现有工具组合而成的 btn-primary 类的示例:
🌐 Here’s what a btn-primary class might look like using @apply to compose it from existing utilities:
<!-- 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>@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.
无论你做什么,不要仅仅为了让东西看起来“更整洁”而使用 @apply。没错,使用 Tailwind 类充斥的 HTML 模板确实有点丑,但在一个有大量自定义 CSS 的项目中进行修改就更糟了。
如果你开始在所有地方都使用 @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:
如果你打算使用 @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.