为什么我推荐所有tailwind使用者使用cn函数

4,068 阅读3分钟

使用cn()有什么好处?

cn()函数实际上就是将twMergeclsx封装到了一起。


twMerge 有什么用?

twMerge 的产生是为了解决样式冲突问题。比如一个button组件,它本身自带color,我们在调用的时候又给它赋值一个color,那么这个color就会作为一个变量传入button组件的className,这个时候我们就有新旧两个color,这就是样式冲突。CSS 的层叠机制确实可以解决样式冲突,但它是在浏览器渲染阶段起作用的。而 twMerge 的价值在于提前优化,在代码层面减少冗余类名,提升可读性和可维护性。

// ❌ 不使用 twMerge
className={
  "px-2 text-red-500",
  "px-4 text-blue-500"  // px-2 和 text-red-500 不会被正确覆盖
}
// 结果: "px-2 text-red-500 px-4 text-blue-500" (样式混乱)

// ✅ 使用 twMerge
import { twMerge } from "tailwind-merge"
className={twMerge(
  "px-2 text-red-500",
  "px-4 text-blue-500"  // 会正确覆盖前面的值
)}
// 结果: "px-4 text-blue-500" (正确的样式)

// 使用 twMerge
className={twMerge(
  "block sm:flex md:grid",  // 基础样式
  "sm:block md:flex"        // 新的响应式样式会正确覆盖
)}
// 结果会正确处理响应式断点的优先级
className={twMerge(
  "p-2 rounded-lg bg-blue-500",
  isError && "bg-red-500 p-4",    // 会正确覆盖 p-2 和 bg-blue-500
  isLarge && "p-6"                // 会正确覆盖之前的 padding
)} 

clsx 有什么用?

clsx的主要作用就是"格式化",让类名更加美观可读。

//不使用clsx
className={
  `px-4 py-2 rounded`,
  isPrimary ? `bg-blue-500 text-white` : `bg-gray-200 text-gray-800`,
}

//使用clsx
className={clsx(`px-4 py-2 rounded`, {
  "bg-blue-500 text-white": isPrimary,
  "bg-gray-200 text-gray-800": !isPrimary,
}

可以通过例子看到,clsx可以将条件判断语句转化为条件对象语句。

除此之外clsx还支持:

// 支持嵌套数组
className={clsx(
  'base',
  ['foo', 'bar'],
  { 'active': isActive },
  [{ 'disabled': isDisabled }]
)}


不止这些

以上均为最重要的特性但cn()的功能不止这些,其中包括:

  • 去除多余空格:
    // 不使用 cn
    className={`base-class ${isActive ? 'active' : ''} ${isDisabled ? 'disabled' : ''}`}
    // 可能输出: "base-class   "(多余的空格)
    
    // 使用 cn
    className={cn("base-class", isActive && "active", isDisabled && "disabled")}
    // 输出干净的结果: "base-class active disabled"  
    
  • 处理falsy值
    // 不使用 cn
    className={`base ${undefined} ${null} ${false}`}  // 会产生 "base undefined null false"
    
    // 使用 cn
    className={cn("base", undefined, null, false)}  // 只输出 "base"
    

如何使用cn()?

首先下载依赖npm install clsx tailwind-merge

然后自定义函数,我的存放位置:/src/lib/utils.ts

import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
 return twMerge(clsx(inputs))
}

任何时候使用cn()吗?

从上面来看cn()无懈可击没有任何缺点,那我们需要任何情况都使用cn()吗?

当然不是,cn() 本质上是个处理函数,必然会对性能造成损耗,所以在简单场景我们无需使用cn()

那推荐何时使用cn()?

推荐在组件上编写样式的时候使用,组件库:shadcn等、自己定义的组件。