Skip to main content

Command Palette

Search for a command to run...

clsx 和 classnames

Published
2 min read
clsx 和 classnames

这两个 npm 包的功能是一样的,都是为了让构造 className 的时候方便带上条件,至于为什么会出现两个包,而且周安装量都有 1000 多万,我是不理解的,所以就抱着好奇的心态,看看他们具体是怎么做的,有什么差异。

classnames

完整代码如下:

const hasOwn = {}.hasOwnProperty;

export default function classNames () {
    let classes = '';

    for (let i = 0; i < arguments.length; i++) {
        const arg = arguments[i];
        if (arg) {
            classes = appendClass(classes, parseValue(arg));
        }
    }

    return classes;
}

function parseValue (arg) {
    if (typeof arg === 'string') {
        return arg;
    }

    if (typeof arg !== 'object') {
        return '';
    }

    if (Array.isArray(arg)) {
        return classNames.apply(null, arg);
    }

    if (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {
        return arg.toString();
    }

    let classes = '';

    for (const key in arg) {
        if (hasOwn.call(arg, key) && arg[key]) {
            classes = appendClass(classes, key);
        }
    }

    return classes;
}

function appendClass (value, newClass) {
    if (!newClass) {
        return value;
    }

    return value ? (value + ' ' + newClass) : newClass;
}

appendClass

这个函数的功能就是拼接字符串,没有过多的类型判断,因为传参的时候已经做好了检查。

value 是已有字符串,newClass 是新的字符串,两者都有存在时就带上空格拼接。

classNames

classNames 接受多个参数,用 arguments 属于兼容老旧浏览器的写法,因为那些浏览器不支持 ES5,没有 spread 语法。

接着遍历每一个参数,如果转换为 boolean 之后是 true,那么把参数传给 parseValue 进行转换成字符串,然后合并起来。

parseValue

这个函数就是对 arg 进行解析了,到这里 arg 肯定是 truly 的。

他会对类型做检查,抛弃了 numberboolean,对于数组类型会当做参数去调用 classNames(得到字符串),对于对象,他会找到非原型链上的属性,检查真假后将 key 拼接到字符串。

clsx

function toVal(mix) {
    var k, y, str='';

    if (typeof mix === 'string' || typeof mix === 'number') {
        str += mix;
    } else if (typeof mix === 'object') {
        if (Array.isArray(mix)) {
            var len=mix.length;
            for (k=0; k < len; k++) {
                if (mix[k]) {
                    if (y = toVal(mix[k])) {
                        str && (str += ' ');
                        str += y;
                    }
                }
            }
        } else {
            for (y in mix) {
                if (mix[y]) {
                    str && (str += ' ');
                    str += y;
                }
            }
        }
    }

    return str;
}

export function clsx() {
    var i=0, tmp, x, str='', len=arguments.length;
    for (; i < len; i++) {
        if (tmp = arguments[i]) {
            if (x = toVal(tmp)) {
                str && (str += ' ');
                str += x
            }
        }
    }
    return str;
}

export default clsx;

toVal

功能上整合了 appendClass 和 parseValue,但没有排除 number,number 会自动转成字符串拼接。

判断出是数组后遍历,然后将每个值递归调用 toVal 去拼接。

对于对象类型,他使用 in 关键字去遍历对象,拿到对象的 key,这里没有判断 key 是否属于对象自身,所以会把原型链上的 key 都带上去。

clsx

一样是用了 arguments 来兼容老浏览器,并且写的语句比较少,几乎是合并定义的。

对比

从参数上看,两者的差异在于“是否会忽略原型链上的属性”:

a = {foo: true}
b = {}
b.__proto__ = a

clsx(b) // 'foo'
classNames(c) // ''

对于重复的值,两者都不会过滤掉,不过 classnames 有提供一个特殊的版本,可以过滤掉重复项。

性能

性能上的差异可以参考 https://www.measurethat.net/Benchmarks/Show/7642/2/classnames-vs-clsx-vs-alternatives。

这个网站做了一系列对比,包括两者和一些替代写法,最终能看出 clsx 比 classnames 的性能要高一些。

总结

所以并没有什么非 clsx 不可的点啊,主要就一些性能上的差异,功能上的差异微乎其微。。。

More from this blog

12 月装机行动记录

(Banner 图文无关) 这几天买了新的装备回来升级配置,除了显卡和散热器,其他都更新了,这两个不更新的原因是太贵了。 由于我不太懂选配置,所以还是让朋友给推荐,我说我的预算在 5000 左右,他就给我转了一个整机,配置大概是这样的: CPU:AMD 9700X 主板:微星 B650M GAMING PLUS WIFI 或者 微星 B650M GAMING WIFI 显卡:木有 内存:英睿达/宇瞻 DDR5 6000 32G 硬盘:1T NVMe PCIe4 SSD 读速 3500M...

Dec 7, 20241 min read
12 月装机行动记录

Homekit + cozylife 插座连接 HA

近期在淘宝上买了个 Homekit + cozylife 的插座,就这种: 一开始只通过 iOS 访问,就是只连接 Homekit,长按开关重置插座,iOS 一扫码就连上了,后来我嫌在外面访问不了,又不想掏钱买苹果的 HomePod,于是就装上了 Home Assistant,打算让设备们都连上 HA,这样就不用交苹果税了。 连接方式还是通过 Homekit,一般来说支持 Homekit 设备都能这样连接,先连上 iOS,然后在 Home App 中移除设备,这时候就能在 HA 中找到设备了:...

Nov 30, 20241 min read
Homekit + cozylife 插座连接 HA

找到了一台祖传的 Ccd 相机

开个玩笑,这台相机其实是我们家在 05 年的时候买的,发票都还在呢,当时花了 4000 块钱!搁现在我都受不了,更别说当年了,看到价格我都震惊了。 相机的型号是索尼的 Cybershot DSC-N1,属于小红书时尚单品 CCD 相机,由于一直放在包装盒里,现在还有 99 新呢。 机子还是正常的,能开机,能拍照,其中一个问题是日期,这款没有 WiFi 功能,所以时间只能保存在本地,不知道是不是 BUG,每次开机都让我重新设置,默认就定在 2005 年 1 月 1 日。 第二个问题是电池,电池应该...

Nov 28, 20241 min read
找到了一台祖传的 Ccd 相机

记录和 ffmpeg 与 LLM 搏斗的两天

要做的 最近在写一个制作视频的功能,就是把 N 个视频合并,然后把对应的 N 张图片,在视频开始的前 5 秒叠加显示出来。 第一口 - diffusion studio 本来我用的是 diffusion studio,这是一个 JS 库,但这玩意性能太差了,因为他要把视频每一帧都读到 canvas 里,数据一多页面就卡住了(为啥要折腾 DOM 呢?) 而且他的 API 十分不好用,作为浏览器脚本你无法读本地数据也就算了,你起码给一个接受纯数据的参数吧,比如 HTML 类型接受源代码,Image ...

Nov 16, 20242 min read
记录和 ffmpeg 与 LLM 搏斗的两天
V

void mian

39 posts