Tailwind的@apply到底有啥用,为啥大佬说别用它?

2,416字
10–15 分钟
in

Tailwind CSS 这玩意儿,圈子里有人爱得深沉,有人一看那一长串类名就头大。不过很多人不知道,Tailwind 里藏了个叫 @apply 的功能,连框架作者 Adam Wathan 都公开说过“几乎别用它”。那这货到底是神器还是鸡肋?今儿个就来扒一扒,顺便整点硬核操作,看看咋样把 @apply 用得飞起。

目录

聊正事之前先捋清楚:@apply 就是让你在 CSS 文件里“复制粘贴”Tailwind 的工具类。比方说写 @apply p-4;,编译出来直接变成 padding: 1rem;。光看这个确实有点傻,但配上自定义工具类,真香程度直接拉满。下面从实际场景出发,手把手带大伙儿把 @apply 玩出花。

@apply是啥

@apply 说白了就是 Tailwind 给 CSS 开的后门,允许在样式表里直接引用那些原子类。好比做菜时候提前调好一碗酱汁,炒菜时直接倒进去,不用每次重新放盐放糖。这玩意儿跟 Sass 的 @mixin@include 简直一个模子刻出来的——先定义一段样式,然后在别处整段引用。

/* 定义一坨样式(用Tailwind的@utility) */
@utility 警告框 {
  background-color: #fef3c7;
  border-left: 4px solid #f59e0b;
  padding: 1rem;
}

/* 别的地方直接用@apply吸过来 */
.alert {
  @apply 警告框;
  margin-bottom: 1rem;
}

不过得留个心眼:@apply 只能用在 @utility 定义好的家伙上,或者直接用 Tailwind 自带的那些类(比如 flexp-4)。千万别在 @apply 里写乱七八糟的嵌套,那玩意儿不认。

整点实战

很多新手一上来就想把所有的样式都塞进 @apply,结果搞得 CSS 文件比 HTML 还长。真正爽的姿势是:先造几个高频复用的布局工具类,然后在 HTML 里直接贴,或者用 @apply 吸到自己的类里。下面整个最常见的——垂直布局和水平布局来回切换。

步骤 1:定义两个基础工具类

在 CSS 文件里(确保 Tailwind 能扫到)写下:

@utility 横着排 {
  display: flex;
  flex-direction: row;
  gap: 1rem;
}

@utility 竖着排 {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

步骤 2:直接在 HTML 里用响应式变体

<div class="竖着排 sm:横着排">
  <div>卡片1</div>
  <div>卡片2</div>
  <div>卡片3</div>
</div>

手机上看是上下摞一块儿,屏幕宽度超过 640px 就自动变成左右并排。那个 sm: 就是 Tailwind 的断点前缀,默认 640px 触发。这里有个小坑:自定义的 @utility 默认不会生成响应式变体,需要在配置文件里把 utilities 的变体打开,或者直接用 Tailwind 3+ 的任意变体写法,比如 sm:横着排 如果没反应,检查下 tailwind.config.js 里有没有 variants: { utilities: ['responsive'] }

步骤 3:不想 HTML 里堆太多类?用 @apply 吸

.我的布局 {
  @apply 竖着排;
}

@media (width >= 640px) {
  .我的布局 {
    @apply 横着排;
  }
}

然后 HTML 里只写 <div class="我的布局">... 就完事儿。这种做法特别适合那些打死也不想在 HTML 里看到工具类的老派 CSS 玩家。

更骚的网格

再整一个网格生成器,只用一行 @utility 搞定响应式栅格。这招用上了 CSS 变量,可玩性直接起飞。

步骤 1:定义网格工具类

@utility 网格简单 {
  display: grid;
  grid-template-columns: repeat(var(--列数), minmax(0, 1fr));
  gap: var(--间距, 1rem);
}

这里 --列数--间距 都是自定义属性(CSS 变量)。minmax(0, 1fr) 是防止内容把格子撑爆的经典写法。

步骤 2:在 HTML 里动态调列数

<div class="网格简单" style="--列数: 3">
  <div>条目1</div>
  <div>条目2</div>
  <div>条目3</div>
</div>

但写内联 style 有点 low。Tailwind 允许用方括号语法直接塞任意值:

<div class="网格简单 [--列数:3]">
  <div>1</div><div>2</div><div>3</div>
</div>

步骤 3:搞成响应式——手机 1 列,平板 3 列

<div class="网格简单 [--列数:1] sm:[--列数:3] md:[--列数:4]">
  <div>卡牌</div>
  <div>卡牌</div>
  <!-- 更多条目 -->
</div>

sm: 是 ≥640px,md: 是 ≥768px。这么一写,HTML 里一眼就能看出布局怎么变,再也不用去 CSS 里翻媒体查询。

如果觉得方括号写法太“魔法”,老路子一样管用:

.响应式网格 {
  @apply 网格简单;
  --列数: 1;
}

@media (width >= 640px) {
  .响应式网格 {
    --列数: 3;
  }
}
<div class="响应式网格">...</div>
场景推荐姿势理由
简单布局HTML直接贴工具类改动不用切文件
复杂组件用@apply包一层复用方便,语义清晰
动态列数任意属性[--列数:3]免写CSS,灵活到爆

实际操作中,建议先把常用的布局模式(水平、垂直、网格、堆叠)做成 @utility,存到一个公共 CSS 文件里。团队里谁要用,要么 HTML 里直接贴类名,要么在自己的 CSS 里 @apply 吸一下。千万别犯强迫症把所有样式都转成 @apply——那还不如回去写普通 CSS。记住一点:@apply 是拿来偷懒的,不是拿来炫技的。碰到需要传参数的情况,比如不同尺寸的头像,直接用 CSS 变量配合 @utility 就能搞定,不用死磕 Sass 那套 @mixin 带参数的写法。