本章通过将之前撸的按钮改成 NextJS 版,进而讨论 Tailwind 的复用原则。

先来回顾一下我们之前撸的按钮:

Tailwind 的复用方式

使用 CSS 复用

使用 CSS 复用可能是很多人第一想法,毕竟像上面那个按钮那样,它的 class 属性太长了。我们可以将其抽取成一个 .btn

<button class="btn">点我</button>

相应的,在 CSS 文件里(比如 nextjs 的 styles/globals.css)定义这个 .btn

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

@layer components {
  .btn {
    @apply bg-blue-600 text-gray-50 px-6 py-2 rounded shadow hover:bg-blue-700 hover:-translate-y-1 hover:shadow-md transition-all duration-500;
  }
}

这些带 @ 前缀的,是 PostCSS 指令(这不是我们的关注点,如果你对 PostCSS 有兴趣,请自行搜索资料进行学习),其中:

  • @tailwind 是 Tailwind 定义的,用于引用其预定义的模块
  • @layer 指定我们自定义的 CSS 隶属于哪个模块。很显然,因为我们是要定义一个按钮,所以它是属于 components
  • @apply 允许在我们自定义的 CSS 里直接使用 Tailwind 预定义的 CSS 类

Tailwind 并不推荐通过 CSS 复用,而是通过下文说的,封装成React组件进行复用。但是,有时候我们避免不了要定义一些辅助性的样式,这时就可以按上面的方法进行定义。

本小节代码:axum-rs-nextjs-with-tailwind-btn-css

使用 React/NextJS 的组件进行复用

首先,我们创建一个 Button 组件:

// components/Button.jsx

export default function Button({ children }) {
  return (
    <button className="bg-blue-600 text-gray-50 px-6 py-2 rounded shadow hover:bg-blue-700 hover:-translate-y-1 hover:shadow-md transition-all duration-500">
      {children}
    </button>
  );
}

然后就可以使用它了:

<Button>点我</Button>

本小节代码:axum-rs-nextjs-with-tailwind-btn-1

复用原则

通过 React/NextJS/Vue 的组件进行复用 Tailwind。当需要一些辅助性的样式时,可以通过 CSS 进行定义。

扩展这个按钮组件

目前我们这个按钮组件只有一种蓝色,在实际项目中需要多种颜色,我们可以通过增加 color 来接收需要的颜色:

<Button>蓝色按钮</Button>
<Button color="red">红色按钮</Button>
<Button color="gray">灰色按钮</Button>

然后你可能在 Button 组件里这样写:

export default function Button({ children,color }) {
  return (
    <button className={`bg-${color}-600 text-gray-50 px-6 py-2 rounded shadow hover:bg-${color}-700 hover:-translate-y-1 hover:shadow-md transition-all duration-500`}>
      {children}
    </button>
  );
}

这是一个严重的错误!

不要通过变量值来拼接 Tailwind 的类名

上面例子中,通过变量 color 的值,直接拼接了两个 tailwind 类:bg-${color}-600hover:bg-${color}-700。这是行不通的!由于 tailwind 的体积非常大,所以利用 PostCSS 进行处理:只打包项目中真实用到的 tailwind 类,这样就能显著地减少项目的体积。

而是通过JS的条件判断给定完整的 Tailwind 的类名

为了更具合理性,我们把上面说的 color 属性改为 type 属性:

然后实现 Button 组件:

export default function Button({ children, type }) {
  let color = 'bg-blue-600';
  let hoverColor = 'hover:bg-blue-700';

  switch (type) {
    case 'red': {
      color = 'bg-red-600';
      hoverColor = 'hover:bg-red-700';
      break;
    }
    case 'gray': {
      color = 'bg-gray-600';
      hoverColor = 'hover:bg-gray-700';
      break;
    }
  }

  const classList = `${color} text-gray-50 px-6 py-2 rounded shadow ${hoverColor} hover:-translate-y-1 hover:shadow-md transition-all duration-500`;

  return <button className={classList}>{children}</button>;
}
  • 针对不同的 type 设置不同的颜色
  • 把具体颜色和其它 CSS 类组成完整的列表
  • 将这个列表传递给 <button> 元素

你或许会有疑问:

  1. 不是说 JSX 里只能用表达式吗,这里怎么跑出来了 switch 语句
    1. 我们说的 JSX 是指具体的 JSX 语句,不是指 *.jsx 文件。
    2. <button className={classList}>{children}</button> 里,只能用 JS 表达式,因为它是 JSX 语句;而在其它地方是 JS 环境,自然可以用包括语句在内的任何 JS 语法
  2. classList不也是在拼接吗
    1. 它拼接的是完整的列表,里面的每一项都是完整的 CSS 类名
    2. 我们说的是不要拼接 CSS 类名

本小节代码:axum-rs-nextjs-with-tailwind-btn-2