cn:shadcn 开源的超高速 Tailwind 类名合并引擎,替代 tailwind-merge 与 clsx

cn:shadcn 开源的超高速 Tailwind 类名合并引擎,替代 tailwind-merge 与 clsx

今天 GitHub Trending 上最值得关注的新项目,来自 shadcn/ui 作者出品的 cn——一个全新的 Tailwind CSS 类名合并与冲突解决方案。它直接替代你项目里几乎必用的 tailwind-mergeclsx 两个工具,API 完全一致、行为完全对齐,却快了 30 倍,在真实大型代码库的回放基准里甚至能快 172 倍

一句话概括它的价值:写组件的代码一行不用改,只要换掉底层的合并函数,界面渲染时处理 className 的开销就能忽略不计。下面详细拆解它是什么、为什么快、以及怎么迁移。

这是什么东西

在 React/Vue/Svelte 等组件里,我们经常要这样拼接多个来源的 class:

cn("px-2 py-1", isActive && "bg-blue-500", { "text-white": isActive })
  • clsx 负责把字符串、布尔值、对象等各种输入拍平成一条干净的空格分隔字符串;
  • tailwind-merge 负责解决 Tailwind 类冲突——比如前面写了 px-2、后面又传 px-4,只保留后者。

绝大多数项目都是用 clsx 先合并、再丢给 tailwind-merge 去重。而 cn 把这两步合并成一个引擎,接口和输出与「clsx + tailwind-merge」完全等价——所以它是 drop-in(即插即用) 的替代品。

cn 拥有以下特点:

  • 零依赖,与框架无关:React、Vue、Svelte、Solid、Astro 或纯服务端模板都能用;
  • 运行环境广:浏览器、Node、Bun、Deno 以及各类 edge runtime 都支持;
  • 不依赖 shadcn/ui,任何 Tailwind CSS v4 项目都能直接接入;
  • Aiden Baishadcn 共同开发维护,MIT 协议开源。

到底快多少

官方基准(每个场景各跑 5 次取最好成绩,pnpm bench 可复现)显示:组件调用最常见的那种形态——cn(基础类, 变体类, 条件 && 额外类)——从 320 纳秒降到 10 纳秒,约 30 倍

更贴近真实的是 pnpm bench:corpus:它把从 58 个开源代码库里抓到的 144,265 次 cn() 调用逐库回放对比,跨 58 个仓库的几何平均结果是比 clsx + tailwind-merge37 倍;重复字符串特别多的真实工作集场景可达 172 倍。

加速的核心秘密是缓存:cn 会「记住」重复调用序列,靠引用一致性直接跳过整段合并工作,所以渲染循环里的调用几乎是零成本。同时它打包后的体积也只有 26 KB(minified),解析成本也更低。

官方还拿 356,000 组差分测试 验证:对任意输入,cn 的输出都和 tailwind-merge 完全一致——放心替换,行为不会漂移。

现在就能用,一分钟迁移

官方明确表示:今天就可以把 tailwind-mergeclsx 换成 cn

全新项目,装好即用,无需任何配置:

npm i cn
import { cn } from "cn"

export function Button({ className, active, ...props }) {
  return (
    

已有 shadcn/ui 项目,用官方 CLI 一键迁移:

npx shadcn@latest migrate cn

或者手动改 lib/utils.ts:把你自封装的那个 cn 小工具直接替换成 cn 包的导出,然后删掉 clsxtailwind-merge 依赖即可。若其它第三方包仍引用它们,官方文档也提供了把这两个包别名(alias)到 cn 的做法,让最终产物只带一份实现。

自定义主题与 tailwind-merge 兼容

需要扩展自定义主题时,cn/config 沿用 tailwind-merge 同名的配置形态:

// tailwind-merge 时代
const twMerge = extendTailwindMerge({
  extend: { classGroups: { "font-size": [{ text: ["hero"] }] } },
})

// cn 时代
import { createCn } from "cn/config"
const cn = createCn({
  extend: { classGroups: { "font-size": [{ text: ["hero"] }] } },
})

自定义校验函数原样可用,fromThemevalidatorsmergeConfigsdefaultConfig、Tailwind v4 的 prefix 都一并支持。绝大多数 tailwind-merge 导出在 cn 里都同名或可对应,只有实验性的 experimentalParseClassName 不在支持范围。

需要注意的坑:

  • cn 支持 Tailwind v4(对应 tailwind-merge v3);如果你还在 Tailwind v3,请继续用 tailwind-merge v2;
  • 批量改名「看起来像 Tailwind 工具类」的字符串时它与 tailwind-merge 行为一致;
  • cn build 预编译模式下,动态拼接的类名(如 "p-" + size)和 Tailwind 本身一样无法检测,需要配合 --safelist

小结

cn 的价值不在于实现了一个全新的合并逻辑——它的语义刻意与 tailwind-merge、clsx 完全对齐,就是想让迁移零成本。真正亮眼的是性能工程:用同样的 API 和输出,把几乎每个组件都在调用的函数做到快一个数量级,还大幅缩小了体积。如果你在用 Tailwind v4 且项目里塞着 clsx + tailwind-merge,花一分钟换成 cn 没有任何风险,属于「白捡的优化」。

项目地址:https://github.com/shadcn-ui/cn · 使用说明见其 README,原理细节在 docs/how-it-works.md

© 版权声明
THE END
喜欢就支持一下吧
点赞14 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容