让你开发更舒适的 Tailwind 技巧( 三 )

cat cafe at Catfel Tower使用 React 和 TypeScript 制作动态可复用组件由于 React 和 TypeScript 的技术组合越来越受欢迎,我们将利用 Tailwind 制作一些酷炫的可复用按钮 。
在此过程中,我们还将了解一些非常小的库来帮助我们:

  • Class-variance-authority 是一个用于制作可复用类的库 , 特别适合于 Tailwind,因为我们不需要在它之外定义任何东西 —— 一个安全、始终有效的工具!
  • Tailwind-merge 和 clsx 是两个帮助我们管理我们在 class-variance-authority 定义中定义的类的库 。它们的使用完全是可选的,因为它们只在一些罕见的情况下有用 。
我们安装这些库 , 然后开始创建我们的按钮!
npm i tailwind-merge clsx class-variance-authority首先,我们创建一个名为 Button.tsx 的文件,其中包含一个基本的按钮组件:
export const Button = () => {return (<button></button>);};然后,我们用 class-variance-authority 定义我们的类 。为此,从库中导入函数 , 如下所示:
import { cva } from "class-variance-authority";函数接受两个参数:
  • 适用于我们可复用按钮所有变体的基本类
  • 包括我们定义的变体及其基本回退情况的对象(以防我们忘记定义某些内容) 。
示例如下:
export const buttonVariants = cva("text-black transition-all border-[1px] border-solid focus:ring-2 text-small",{variants: {variant: {filled: "bg-yellow-600 ring-black border-yellow-600 hover:bg-white",outline: "border-yellow-600 ring-black bg-white hover:bg-yellow-600",},size: {small: "px-[1.5rem] py-[1rem] rounded-sm",medium: "px-[2rem] py-[1.5rem] rounded-md",},},defaultVariants: {variant: "filled",size: "small",},});然后 , 我们定义默认变体:variant 将是 filled,size 将是 small 。
由于我们的组件是可复用的,我们需要以某种方式将这些属性传递给它 —— 我们将通过 props 来做,并使它们类型安全 。
首先,我们将声明一个基本接口:
interface IButtonProps {}然后,我们将从 class-variance-authority 的泛型接口 VariantProps 扩展,可以通过 cva 函数导入它 。它接受我们定义的 buttonVariants 的 typeof 并使 props 类型安全,因此我们不会定义我们没有的 big 尺寸:
interface IButtonProps extends VariantProps<typeof buttonVariants> {}最后,我们还将从 React 的 HTMLAttributes 扩展,它也是泛型并接受 HTML 元素的类型 。它将使我们的 props 包括按钮的所有基本 HTML 属性 , 如 onClick,以及我们类型中定义的 React children:
interface IButtonPropsextends HTMLAttributes<HTMLButtonElement>,VariantProps<typeof buttonVariants> {}完成所有这些后,我们可以这样对组件进行类型化并使用其 props:
export const Button = ({variant,size,className,children,...props}: IButtonProps) => {return (<button className={buttonVariants({ size, variant })} {...props}>{children}</button>);};现在 , 当我们传递 variant 和 size 时,按钮将改变其视图!看:
export default function App() {return (<><Button variant="filled">填充按钮</Button><Button variant="outline">轮廓按钮</Button></>);}但是 , 当我们需要稍微调整按钮时怎么办?为此,我们有我们的 className 属性 , 可以用两种不同的方式使用:
我们在现场将其与我们的 buttonVariants 函数合并,使用模板字面量:
<button className={`${buttonVariants({ size, variant })} ${className}`} {...props}>我们使用之前提到的两个库来确保我们的类没有重复:例如,当我们有 mx-2 和 my-2 时,它将被转换为 m-2
为此,让我们在其他地方定义一个小函数,如下所示:
import clsx, { ClassValue } from "clsx";import { twMerge } from "tailwind-merge";export const clsxm = (...classes: ClassValue[]) => twMerge(clsx(...classes));这里 , 它接受任何数量的类并将它们与 tailwind-merge 库中的函数合并 。现在,我们可以用该函数而不是模板字面量合并我们的类:


推荐阅读