前言
如果翻阅 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 样式但覆盖不去 /> )}
要解决这个问题有两种可能的方法:
以上两种方法都有各自的问题,所以才需要 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'
// Objectsclsx({ foo:true, bar:false, baz:isTrue() });//=> 'foo baz'
// Objects (variadic)clsx({ foo:true }, { bar:false }, null, { '--foobar':'hello' });//=> 'foo --foobar'
// Arraysclsx(['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
延伸阅读
- cn() - Every Tailwind Coder Needs It (clsx + twMerge) - Byte Grad
- What is it for - tailwind-merge GitHub
- 01__clsx__function__main__.pdf - clsx-Documentation