发布于 2026-01-06 6 阅读
0

为什么在下一个项目中使用 Tailwind CSS

为什么在下一个项目中使用 Tailwind CSS

Tailwind CSS是一个底层、可定制的 CSS 框架,目前发展迅猛。它经常被拿来与 Bootstrap 比较,但它们本质上截然不同。Bootstrap 提供预先设计好的组件,而Tailwind则提供构建模块,让开发者能够快速构建设计。

在接触 Tailwind 之前,我先学习了 CSS,然后转向 SCSS,最后又学习了 Styled Components。在我的学习过程中,每种技术都给我带来了很多宝贵的经验,但没有一种技术能像 Tailwind 一样,带给我如此巨大的满足感和效率提升。

漫长的旅程

对于初学者来说,编写纯 CSS 非常有利,因为可以充分理解并记住实现特定布局所需的规则。即使是高级开发者,也能通过编写完全符合需求的自定义 CSS 来获得非常好的效果。然而,这些规则很可能无法扩展。用户界面中的每个元素都需要自己的一套规则,跟踪数百条规则可能会变得令人望而生畏,难以维护。但这并不意味着无法管理它们。通过适当的练习和自律,这些规则可以被很好地组织和维护。

为了解决这个问题,我从 CSS 迁移到了 SCSS,并采用了7 合 1 架构。起初,我非常喜欢这种将代码清晰地划分到 7 个文件夹中的方式,每个文件夹分别代表网站的特定组件和部分。此外,它还带来了变量、函数和嵌套规则等新特性,开启了无限可能。这使得网站的样式更容易查找和维护。然而,我希望将样式嵌入到 JavaScript 代码中,这样就不用频繁地在不同文件之间切换。因此,我开始使用像Styled Components这样的 CSS-in-JS 解决方案。

在项目中无法使用 Tailwind 时,Styled Components 是我的首选。它是一个非常出色的库,因为它允许我将所有样式精确地应用到需要的地方。此外,它还能确保应用程序只使用所需的样式,而不是在每个页面上都导入所有样式。尽管它比其他解决方案有了很大的改进,但它仍然存在一个问题:重复性。我感觉自己一遍又一遍地编写相同的规则,浪费了大量时间。而且,我的样式表也变得越来越庞大。当我意识到这一点并发现 Tailwind 后,我决定放弃 Tailwind,并且从此再也没有后悔过。

句法

Tailwind 的语法非常简单:只需在 HTML 或 JSX 元素后添加代表一个或多个 CSS 声明的类名即可。如下所示:

<div class="max-w-sm mx-auto flex p-6 bg-white rounded-lg shadow-xl">
  <div class="flex-shrink-0">
    <img class="h-12 w-12" src="/img/logo.svg" alt="ChitChat Logo">
  </div>
  <div class="ml-6 pt-1">
    <h4 class="text-xl text-gray-900 leading-tight">ChitChat</h4>
    <p class="text-base text-gray-600 leading-normal">You have a new message!</p>
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

这将生成以下内容:

使用 TailwindCSS 创建的示例通知

每个类名代表一个或多个CSS 声明。例如,flex 代表display: flex;p-6 padding: 1.5rem、mx-automargin-left: auto; margin-right: auto;等。

这种范式被称为“实用性优先”。它指的是利用一组有限的基本实用工具来构建复杂的组件。

与自定义 CSS 相比,使用这种方法,Tailwind 可以获得诸多优势。

小号款式

使用 Tailwind 的首要好处是编写的 CSS 不会线性增长。通过使用元素间共享的实用类名,可以确保打包后的文件大小保持一致。此外,Tailwind 与 Purge CSS 结合使用,可以移除所有未使用的样式,从而避免代码库因过多的类而变得臃肿。

使用 Tailwind CSS 的网站 CSS 包看起来会像这样:

图表展示了 TailwindCSS 包大小的节省情况

样式表在某个阶段可能会比较高,但未来随着功能的增加,这些样式表将保持不变,从而节省大量 KB 空间。

Facebook也采用了类似的方法,不过他们使用的是自定义的CSS-in-JS 库,该库利用了原子 CSS,这与 Tailwind 的实用性优先方法非常相似。通过这种方法,他们将整个网站的 CSS 包大小从 413kb 减少到了 74kb,降幅高达约 82%!现在,我们无需创建自定义库也能拥有这种强大的功能。

💡 原子 CSS 类只能有一个唯一的 CSS 声明。这与实用性优先的 CSS 有很大不同,在实用性优先的 CSS 中,类可以有多个声明。

认知负荷降低

Tailwind 的设计理念是组件化。它能更轻松地将网站元素拆分成更小的组件,避免用对象或无关的 CSS 类污染代码库。此外,所有类都内联到组件中,使代码更易于阅读和理解。

例如,使用 Styled Components 构建的这个导航栏组件:

const Navbar = () => {
  return (
    <Wrapper>
      <Items>
        <NavItem label="Home" icon={<Home size={16} />} />
        <NavItem label="Blog" icon={<Bookmark size={16} />} />
        <NavItem label="About" icon={<User size={16} />} />
      </Items>
    </Wrapper>
  )
}

const Wrapper = styled.nav`
  position: fixed;
  width: 100vw;
  bottom: 0;

  justify-content: center;
`

const Items = styled.ul`
  flex: 1;
  padding: 0 2rem;

  justify-content: space-around;
`

const NavItem = ({label, icon, ...otherProps}) => (
  <Item {...otherProps}>
    <span>{icon}</span>
    {label}
  </Item>
)

const Item = styled.li`
  display: flex;
  flex-direction: column;
  align-items: center;

  font-size: 1.2rem;
`
Enter fullscreen mode Exit fullscreen mode

我认为这段文字组织得很好,读完之后很容易理解。

让我们来看看 Tailwind 的实现方式:

const Navbar = () => {
  return (
    <nav className="fixed bottom-0 justify-center w-screen">
      <ul className="justify-around flex-1 px-8">
        <NavItem label="Home" icon={<Home size={16} />} />
        <NavItem label="Blog" icon={<Bookmark size={16} />} />
        <NavItem label="About" icon={<User size={16} />} />
      </ul>
    </nav>
  )
}

const NavItem = ({label, icon, ...otherProps}) => (
  <li className="flex flex-col items-center text-xl" {...otherProps}>
    <span>{icon}</span>
    {label}
  </li>
)
Enter fullscreen mode Exit fullscreen mode

Tailwind 从一开始就占用空间更少。此外,它的样式表也更容易理解,因为开发者在浏览代码时会同时读取样式表。无需跳转到特定文件即可查看样式表。

在这个小例子中,可能不太容易看出在大文件中这会变得多么繁琐。但在包含多个组件的大型代码库中,组件间各自独立的样式简直是一场噩梦。与 Tailwind 相比,理解每个元素如何构成用户界面需要花费更多的时间。

你可能会问,如何才能充分利用 Styled Components 的主要优势:允许在样式中使用 JavaScript。这取决于解决方案的具体情况。

可以利用内联样式对象或条件类名。

// Inline object
<CustomComponent styles={{ color: fontColorByBackground('#000') }}/>

// Conditional classes
import clsx from "clsx"
<CustomComponent
  className={clsx('flex', {
    'text-black': luminance > 0.18,
    'text-white': luminance < 0.18,
  })}
/>
Enter fullscreen mode Exit fullscreen mode

然而,对于某些团队来说,这可能并非最具吸引力的解决方案。好消息是,由于我们使用的是 CSS,因此可以同时使用 Tailwind 和 Styled Components,而无需额外成本(Emotion CSS 属性CSS 模块可能更符合 Tailwind 的设计理念)。

💡 请记住,Tailwind 是主流解决方案,因此您可以充分利用它的优势。对于需要大量 CSS/JavaScript 操作的情况,Styled Components 是可选的。此外,这也使得希望逐步迁移到 Tailwind 的代码库更容易上手。

更佳的成分

Tailwind 默认引导开发者以组件化的方式思考。它促使我们保持代码简洁,避免重复。这得益于它占用空间小,便于实现布局;它能让我们注意到重复或臃肿的样式;以及将样式拆分成小组件后,样式更容易阅读和记忆。

可以将这种做法想象成为网站的每个功能或部分创建 CSS 类名。只不过,我们不是用 CSS 来实现,而是用 HTML 来实现。例如,如果网站有一个“首页横幅”(Hero),那么最好创建一个组件来代表这个横幅部分。同样地,我们也可以用类似的方法处理博客文章、导航、页眉等等。

我们来看下一段代码:

const IndexPage = () => {
  return (
    <section className="container p-2 mx-auto lg:p-8 xl:p-0">
      <h1 className="font-bold text-white text-11xl sm:text-12xl">Blog.</h1>
      <div className="flex flex-col mx-auto space-y-8 md:grid md:row-gap-6 md:col-gap-8 md:grid-cols-2">
        {posts.map(
          ({ frontmatter: { title, description }, slug, hasCoverImage }, i) => {
            const isFirstItem = i === 0;

            return (
              <div
                key={slug}                
                className={clsx(
                  "w-full bg-white rounded-md font-body relative overflow-hidden",
                  isFirstItem && "md:col-span-2",
                  !isFirstItem && "md:col-span-1"
                )}
              >
                <a href="#">
                  {hasCoverImage && (
                    <img
                      className="object-contain w-full rounded-md md:object-cover"
                      src={src}
                      alt="cover image"
                    />
                  )}
                  <header className="flex flex-col items-center p-12 space-y-4 text-center bg-white rounded-md">
                    <h1 className="max-w-6xl text-5xl font-bold">{title}</h1>
                    <p className="max-w-5xl text-2xl font-medium">
                      {description}
                    </p>
                  </header>                
                </a>
              </div>
            );
          }
        )}
      </div>
    </section>
  );
};
Enter fullscreen mode Exit fullscreen mode

你会发现,除非仔细阅读,否则很难理解它的意图。类名杂乱无章,HTML元素缺乏连贯性。人们立刻就会意识到,肯定有更好的方法来表示它。

如果我们把它分解成各个组成部分,就能立即看到它的好处:

const IndexPage = ({ posts }) => {
  return (
    <div className="container p-2 mx-auto lg:p-8 xl:p-0">
      <h1 className="font-bold text-white text-11xl sm:text-12xl">Blog.</h1>
      <Posts posts={posts} />
    </div>
  );
};

const Posts = ({ posts }) => (
  <div className="flex flex-col mx-auto space-y-8 md:grid md:row-gap-6 md:col-gap-8 md:grid-cols-2">
    {posts.map((postInfo, i) => {
      const isFirstItem = i === 0;

      return (
        <PostCard key={postInfo.slug} isFirstItem={isFirstItem} {...postInfo} />
      );
    })}
  </div>
);

const PostCard = ({
  frontmatter: { title, description },
  hasCoverImage,
  isFirstItem,
}) => (
  <div    
    className={clsx(
      "w-full bg-white rounded-md font-body relative overflow-hidden",
      isFirstItem && "md:col-span-2",
      !isFirstItem && "md:col-span-1"
    )}
  >
    <a>
      {hasCoverImage && (
        <img
          className="object-contain w-full rounded-md md:object-cover"
          src={src}
          alt="cover image"
        />
      )}
      <header className="flex flex-col items-center p-12 space-y-4 text-center bg-white rounded-md">
        <h1 className="max-w-6xl text-5xl font-bold">{title}</h1>
        <p className="max-w-5xl text-2xl font-medium">{description}</p>
      </header>
      <div className="absolute top-0 right-0 px-6 py-8 mr-8 text-white bg-black">
        <p className="block font-semibold">Read more</p>
      </div>
    </a>
  </div>
);
Enter fullscreen mode Exit fullscreen mode

这样不仅短期内更容易阅读,而且当我们再次查看时,也能更快地理解。此外,当代码规模增大,需要重用“阅读更多”之类的元素时,我们可以轻松创建组件并在不同元素之间共享。

ℹ 这种做法并非仅适用于 Tailwind,而是适用于所有情况。不过,Tailwind 的类名与具体实现无关,因此更容易判断何时应该这样做。而 CSS 或 CSS-in-JS 库则恰恰相反:它们要求创建的类或对象只能用于特定的上下文。

更高的生产力

毫不奇怪,编写 Tailwind 类比编写 CSS 或 CSS-in-JS 更快。在样式表中添加选择器声明时,我们需要输入属性和值,并在末尾添加冒号。好在 IDE 和文本编辑器会自动提供自动补全和格式化功能。但是,我们仍然需要手动输入选择器和部分声明才能触发自动补全。而 Tailwind 通过直接内联编写样式,从长远来看,大大提高了开发效率。

此外,Tailwind 还具有称为变体的功能,变体是可以添加到类中的前缀,代表特定的CSS 伪类响应式断点或自定义插件。

以媒体查询为例。在普通的 CSS 中,我们会这样写:

/* Small (sm) */
@media (min-width: 640px) { /* ... */ }

/* Medium (md) */
@media (min-width: 768px) { /* ... */ }

/* Large (lg) */
@media (min-width: 1024px) { /* ... */ }

/* Extra Large (xl) */
@media (min-width: 1280px) { /* ... */ }
Enter fullscreen mode Exit fullscreen mode

这段代码乍看之下似乎很容易读懂。但是,想象一下如果有数百个类,很快就会变得难以阅读,修改起来也会很慢。

现在,我们来看看 Tailwind 的:

<!-- Width of 16 by default, 32 on medium screens, and 48 on large screens -->
<img class="w-16 md:w-32 lg:w-48" src="...">
Enter fullscreen mode Exit fullscreen mode

这里我们为每个断点定义了宽度。这样做的好处在于,我们将样式限定在元素内部。这样一来,以后移除或修改声明就容易得多,因为它不会与数百条规则冲突。

同样,伪类在 Tailwind 中比在 CSS 中更容易实现。这并非因为可维护性或可读性,而是因为编写起来更方便。添加前缀比添加一条全新的规则要快得多:

<input class="w-16 bg-orange-500 hover:bg-blue-500 focus:bg-red-500" />

<!-- 
Translates to: 

.block {
    width: 4rem;
    background: #ed8936;
}

.block:hover {
    background: #4299e1;
}

.block:focus {
    background: #f56565;
}

 -->
Enter fullscreen mode Exit fullscreen mode

最后,我们还有自定义插件。它们允许您添加自定义前缀,从而增强类的行为。例如,它们允许开发者根据条件(例如暗黑模式)添加类,或者通过赋值来覆盖其他类!important

<!-- Dark mode (Coming in v2) -->
<div className="bg-black dark:bg-white"/>

<!-- Important (https://github.com/chasegiunta/tailwindcss-important) -->
<div className="!bg-red"/>
Enter fullscreen mode Exit fullscreen mode

一致性

除了能更快地编写样式之外,Tailwind 还预置了大量用于尺寸和颜色的类,这可以减少甚至完全消除实现设计系统的需要。这也意味着,对于经验丰富的开发者来说,使用 Tailwind 在现有项目中学习样式所需的学习曲线也更平缓。

💡 你可能会问它在各种浏览器上的兼容性如何。由于 Tailwind 是一个非常底层的框架,这意味着对旧版浏览器的支持取决于开发者,而不是像 Bootstrap 那样由浏览器实现。

高度定制化

Tailwind 为开发者提供了一个功能丰富的默认主题,非常适合基于 Bootstrap 的项目。不过,它也鼓励开发者通过配置tailwind.config.js文件来扩展或修改这些类。

Tailwind 的配置文件允许开发者直接修改 Tailwind 的设置。任何更改都会反映在最终的样式表中。它还允许添加自定义插件,这为第三方开发者开启了无限可能。

许多开发者在刚开始使用 Tailwind 时都会抱怨其样式过于单一。与纯 CSS 不同,Tailwind 似乎限制了样式的自定义,因为其默认类非常丰富,而且看起来像是遵循 Bootstrap 之类的规范。然而,由于 Tailwind 中的类通常只代表一个 CSS 声明,因此可以轻松地扩展、添加或删除它们。

另一方面,开发人员有时拒绝扩展此配置,因为他们不想深入研究文档,或者只是想为一个组件向一个类添加多个声明。

由于 Tailwind 使用 PostCSS 作为预处理器,这意味着它的功能可以扩展到配置文件之外的更广泛领域。大多数代码库都会有一个 CSS 文件,通过 PostCSS 导入 Tailwind 样式。

在这个文件中,既可以创建普通的 CSS 类,也可以使用 Tailwind 提供的丰富类库。例如:

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer components {
  .btn-blue {
    @apply bg-blue-500 text-white font-bold py-2 px-4 rounded;      
  }
  .btn-blue:hover {
    @apply bg-blue-700;
  }
}
Enter fullscreen mode Exit fullscreen mode

总而言之,这意味着所有可以用 CSS 实现的功能都可以在 Tailwind 中实现!

绝佳的升级途径

Tailwind 的未来一片光明。它频繁推出更新,新增了适用于现代浏览器的 CSS 属性、新的前缀、便捷的 PostCSS 指令、新的类等等。

例如,在即将发布的 2.0 版本中,我们将为深色模式添加一个新的前缀。以前,添加深色模式需要一些变通方法,但现在只需dark:在应用于深色模式的类名前添加前缀,即可轻松实现原生添加。

<div class="bg-white text-black dark:bg-black dark:text-white dark:hover:text-gray-300"/>
Enter fullscreen mode Exit fullscreen mode

此外,在 1.7 版本(目前为 1.9.6 版本)中,我们新增了文本背景裁剪功能,用于创建炫酷的渐变文本。这些只是众多显著提升开发体验和效率的更新中的几个例子。

渐变文字

缺点

Tailwind CSS 具有诸多优势,例如提升性能、一致性和生产力。然而,这并不意味着它完美适用于所有代码库或开发团队。

Tailwind 的一个缺点是可读性。起初可能并不明显,但当一个元素添加了相当多的类时,对于一些开发者来说,阅读起来可能会非常吃力。就我个人而言,我并没有遇到太多这方面的问题,而且我相信,只要组件抽象得当,这个问题就能轻松解决。尽管如此,许多开发者仍然会把这视为一个缺点。

Tailwind 的另一个弱点是创建复杂的动画。虽然它内置了一些简单的动画,并且可以通过配置添加更多动画,但要实现复杂的动画编排却非常困难。

要创建复杂的动画,需要为每个元素创建一个类名。这比直接使用 CSS 更费力。幸运的是,使用纯 CSS 的样式表、像 Framer Motion 这样的动画库,或者像 Styled Components 这样的 CSS-in-JS 库,都可以轻松解决这个问题。我认为这些方法在 Tailwind 框架下都是有效且值得推荐的,但是,有些开发者可能更喜欢直接使用纯 CSS 来实现动画,而不需要任何库。

另一个缺点是需要学习一套固定的命名规则。这有利有弊。一方面,固定的命名风格意味着一旦掌握,相关知识就能在不同项目间迁移,从而大幅提高效率。另一方面,必须投入时间学习所有细微差别及其运作方式,才能创造出有用的成果。

我认为,如果了解 CSS,学习 Tailwind 的命名规范会非常容易,因为它的类名与纯 CSS 非常相似。此外,他们提供的专属扩展程序还具备自动补全功能,方便开发者学习和记忆类名。

然而,有些团队仍然不愿意引入需要学习曲线的框架,尤其是在时间非常宝贵的情况下。

结论

Tailwind 作为底层 CSS 框架,已成为我首选的样式解决方案。它解决了我在尝试各种 CSS 方案时遇到的大部分问题。它提高了我的工作效率,减小了应用程序的打包体积,使我的代码更加一致,并改变了我的开发模式。

尽管 Tailwind 对我以及许多其他开发者来说都非常有效,但这并不意味着它是每个项目的完美解决方案。在考虑采用它之前,必须评估它对代码库的益处以及对团队的影响。

未来,我预见Tailwind将会发展壮大,并被应用于许多项目中,即使这些项目已经采用了现有的CSS解决方案。我已经看到许多现代项目开始这样做,例如Vercel电商模板

归根结底,前端开发人员最终还是要使用 CSS。这意味着只要我们保证可维护性、可扩展性和性能,就可以融合不同的解决方案。考虑到这一点,Tailwind 凭借其诸多优势,无疑是一个值得考虑的解决方案。

想了解更多最新的网站开发内容,请在TwitterDev.to上关注我!感谢阅读!😎


你知道我有电子报吗?📬

如果您想在我发布新博客文章时收到通知,并每周收到很棒的资源以在 Web 开发方面保持领先地位,请访问https://jfelix.info/newsletter

文章来源:https://dev.to/joserfelix/why-use-tailwind-css-for-your-next-project-39hp