Why Does Shadcn use cn() Helper Functions?

twMerge() + clsx() = cn() ?Shadcn 为什么要用这些辅助函数?

前言

如果翻阅 Shadcn🔗 组件集的组件会发现大量使用到一个辅助函数 cn 如下:

import { type ClassValue, clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}

它其实是两套库(tailwind-merge🔗clsx🔗)的组合,用于更方便构建组件中的 Tailwind 样式。为什么会需要额外的库和方法来构建组件样式呢?

Tailwind Merge

如果将 Tailwind 和基于组件的 UI 渲染器(如 React 或 Vue)一起使用会遇到样式覆盖不去的问题,不管最终样式字符串如何组合,Tailwind 会在生成 utility class 过程中把所有可能用到的 class 输出成一份静态 CSS,其中 定义顺序影响样式的优先级

function MyGenericInput(props) {
const className = `px-2 py-1 ${props.className || ''}`
return <input {...props} className={className} />
}
function MyOneOffInput(props) {
return (
<MyGenericInput
{...props}
className="p-3" // ← 传入 class 想修改 padding 样式但覆盖不去
/>
)
}

要解决这个问题有两种可能的方法:

  1. 新增组件参数用于切换内部样式🔗
  2. 使用 Tailwind important 修饰符🔗

以上两种方法都有各自的问题,所以才需要 Tailwind Merge 协助:

twMerge('p-5 p-2 p-4') // → 'p-4'

tailwind-merge 依靠 ~7 kB 的配置文件决定如何融合 Tailwind 相关的样式字符串,具体融合规则可以参考合并规则文件🔗

clsx

如果将 Tailwind 和基于组件的 UI 渲染器(如 React 或 Vue)一起使用,常常要根据条件动态切换 CSS class,原生写法很快就变得又长又难读:

<div
className={
{/* 留意 Class 间空格留白需组合正确 */}
"p-2 " +
(isActive ? "bg-blue-500" : "bg-gray-300") +
(isDisabled ? " opacity-50" : "")
}
/>

clsx 既可以简化样式开发流程,也能避免错误的 falsy 状态被组合进去:

<div
className={clsx("p-2", {
"bg-blue-500": isActive,
"opacity-50": isDisabled,
})}
/>

规则如官方 Usage 文件🔗

// Strings (variadic)
clsx('foo', true && 'bar', 'baz');
//=> 'foo bar baz'
// Objects
clsx({ foo:true, bar:false, baz:isTrue() });
//=> 'foo baz'
// Objects (variadic)
clsx({ foo:true }, { bar:false }, null, { '--foobar':'hello' });
//=> 'foo --foobar'
// Arrays
clsx(['foo', 0, false, 'bar']);
//=> 'foo bar'
// Arrays (variadic)
clsx(['foo'], ['', 0, false, 'bar'], [['baz', [['hello'], 'there']]]);
//=> 'foo bar baz hello there'
// Kitchen sink (with nesting)
clsx('foo', [1 && 'bar', { baz:false, bat:null }, ['hello', ['world']]], 'cya');
//=> 'foo bar hello world cya'

总结

虽然以上套件都着重在非常简单的小问题,但在大型 UI 项目可用于提升开发体验。最后我对这些缩写有很大的疑问,写完这篇文章才大致猜出缩写的意涵:

  • twMerge = Tailwind Merge
  • clsx = Class Mix
  • cn = Class Name

延伸阅读