Skip to main content

Command Palette

Search for a command to run...

clsx 和 classnames

Published
•2 min read•View as Markdown
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 的。

他会对类型做检查,抛弃了 number 和 boolean,对于数组类型会当做参数去调用 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