classnames (opens new window) npm 包
将不同数据类型,比如字符串、数字、object、array 转换成标准 CSS 的 class
字符串类型
可用于 react 或原生 js 中 class 拼接
当然也可用于 Vue
只是没必要,因为 Vue class 功能与之类似,而且相当强大
一共 3 个版本:
默认版
:使用index.js
,缺点不支持 css module、去重dedupe version
去重版本:使用dedupe.js
缺点比默认版本处理速度慢 5x
bind version
css module 版本:使用bind.js
在默认版本基础上可动态改变this
指向,缺点不支持去重
源码讲解只涉及默认版
,因为比较常用
# 结合 React 示例
注意 2 点:
0、false、undefined、null、''
falsely 值不会出现在结果中- 默认版本,重复的值,不会自动去重,若去重可使用
dedupe
版本
import classNames from "classnames";
const Btn = () => {
// 其中 classNames 函数中传入的参数,放入数组中 [...],也是可以的
const cls = useMemo(
() =>
classNames(
// class 字符串类型
"btn",
// class 数组类型
["btn-primary"],
// class 对象类型
{
"text-warning": true,
},
"btn",
// 以下 class 无效
0,
false,
undefined,
null,
""
),
[]
);
console.log(cls); // btn btn-primary text-warning btn -> 同名的 class 不去重
return <button className={cls}>button</button>;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 源码初探
所有的代码处于严格模式下,包裹在匿名自执行函数中
在应用中引入 classnames
模块时
模块会自执行,将模块以 cjs、amd、global
方式导出
比较有意思的是 {}.hasOwnProperty
引用一个判断对象属性归属的快捷方式
(function() {
"use strict";
var hasOwn = {}.hasOwnProperty;
// ... 关键省略部分
// CMD 规范实现,用于 node、webpack 等
if (typeof module !== "undefined" && module.exports) {
classNames.default = classNames;
module.exports = classNames;
} else if (
typeof define === "function" &&
typeof define.amd === "object" &&
define.amd
) {
// AMD 规范实现,用于 seajs
define("classnames", [], function() {
return classNames;
});
} else {
// 挂载于 global,这里是 window 对象上,用于浏览器
window.classNames = classNames;
}
})();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 源码深入
遍历传入 classnames
函数的参数数组 arguments
处理不同数据类型的 class
将处理的结果收集到 classes
数组中
最后通过 join(' ')
方法将 classes
转换为标准的 class
并返回
function classNames() {
var classes = [];
for (var i = 0; i < arguments.length; i++) {
var arg = arguments[i];
// 若参数是 falsely 值,跳过此次循环,不收集
if (!arg) continue;
var argType = typeof arg;
// 字符串或数字类型的 class 直接 push 到数组中
if (argType === "string" || argType === "number") {
classes.push(arg);
} else if (Array.isArray(arg)) {
if (arg.length) {
// class 是数组且长度 > 0,递归调用,this 在这里没意义,指向 null
var inner = classNames.apply(null, arg);
if (inner) {
classes.push(inner);
}
}
} else if (argType === "object") {
// class 对象重写的 toString 方法
// 多出现于三方类库中
// Issue: https://github.com/JedWatson/classnames/issues/278
if (
arg.toString !== Object.prototype.toString &&
!arg.toString.toString().includes("[native code]")
) {
classes.push(arg.toString());
continue;
}
// class 是对象,且 key 是在 class 本身且值不为 falsely,push 到数组中
for (var key in arg) {
if (hasOwn.call(arg, key) && arg[key]) {
classes.push(key);
}
}
}
}
// 循环结束,将结果转换成标准 class 字符串返回
return classes.join(" ");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 对比 Vue class 绑定
Vue class 绑定 (opens new window)
与 classnames
用法有诸多相似
Vue class 绑定
// 绑定字符串,如果确定 class,可以不使用 class 绑定语法的
<div :class="`active`"></div>
// 绑定对象, isActive 为真时,class 为 active
<div :class="{active: isActive}"></div>
// 绑定数组,结果同上
<div :class="[isActive ? 'active' : '']"></div>
// 数组中嵌套对象,结果同上
<div :class="[{active: isActive}]"></div>
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
classnames
支持字符串、对象、数组数据类型,然而 Vue 内置的 class 绑定中天然支持
所以为什么 Vue 中可以不用 classnames
# 小结
结合 react
说明了 classnames
库的使用方式
通过源码阐明了实现原理
简单地说:将字符串、数字、对象、数组方式的数据结构转换为 class 支持字符串拼接方式
最后对比了 Vue 中 class 绑定,表明 Vue 中天然对 class 支持较为强大
所以不建议使用 classnames
扫一扫,微信中打开