与 classnames (opens new window) 类似的库
用于处理 html 的 class 拼接
但有以下优点
- 足够小
228B
- 足够快, classnames,至少 1 倍
开发有性能要求,可以选择 clsx (opens new window)
另外对浏览器有要求,因为使用 Array.isArray
,所以只支持 IE9+ 浏览器
# 源码初探
源码构建 (opens new window)过程使用自己开发的 Nodejs 脚本实现 🎉
并非是 rollup 或 webpack 一堆配置麻烦,自实现足够简洁
// @ts-check
const fs = require('fs');
const zlib = require('zlib');
const { minify } = require('terser');
const pkg = require('../package.json'); // Nodejs 内置支持读取 json 文件
// 构建前,没有 dist 目录,创建
if (!fs.existsSync('dist')) fs.mkdirSync('dist');
/**
* @param {string} file
* @param {string} source
*/
// 源码写入对应的文件
function write(file, source) {
let isModule = !source.startsWith('!function');
// 源码压缩
let result = minify(source, {
module: isModule,
compress: true,
});
// 源码写入命名文件中
fs.writeFileSync(file, result.code);
// 测试压缩后源码字节大小
console.log('~> "%s" (%d b)', file, zlib.gzipSync(result.code).byteLength);
}
// 读取源码
let input = fs.readFileSync('src/index.js', 'utf8');
// copy for ESM - 源码写入 package.json 中的 module 字段
// 源码默认是 ES module,所以直接写入
write(pkg.module, input);
// transform ESM -> CJS exports - 源码写入 package.json 中的 main 字段
// 源码非 CJS 模块,所以将 ES module 通过正则替换为 CJS 模块
write(pkg.main, input.replace('export function', 'function').replace(
'export default clsx;',
'module.exports = clsx;\n'
+ 'module.exports.clsx = clsx;'
));
// transform ESM -> UMD exports ,将源码处理成通用模块,通过全局 clsx 调用或 在 AMD 模块中调用
input = input.replace('export function', 'function').replace('export default clsx;', 'return clsx.clsx=clsx, clsx;');
write(pkg.unpkg, '!function(global,factory){"object"==typeof exports&&"undefined"!=typeof module?module.exports=factory():"function"==typeof define&&define.amd?define(factory):global.clsx=factory()}(this,function(){' + input + '});');
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
43
44
45
46
47
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
43
44
45
46
47
以上就是构建的全部过程,除了简洁还是简洁
如果 UMD 模块处于 use Stirct
模式下就更好了
# 源码深入
- 导出 clsx 模块
- 处理不同类型的 class
- 对于 falsy (opens new window) 一律忽略不处理
关键代码是
// 如果当前的 class 是 true,添加一个空格
// 然后再拼接后面的 class,结果 class 就是 "btn btn-primary xx"
str && (str += ' ')
str += x
1
2
3
4
2
3
4
# 导出 clsx 模块
所有导出都是 ESM
// 命名导出,通过 import {clsx} from ... 使用
export function clsx() {
var i=0, tmp, x, str='';
// 遍历参数,使用 toVal 处理,为 true 拼接 class,false 不处理
while (i < arguments.length) {
if (tmp = arguments[i++]) {
if (x = toVal(tmp)) {
str && (str += ' ');
str += x
}
}
}
return str;
}
// 默认导出,通过 import clsx from '...' 使用
export default clsx;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 处理不同的类型的 class
clsx 支持字符串、数字、数组、对象,数组嵌套,不支持对象嵌套
对于对象的处理使用 for...in
不是特别友好
因为它会遍历对象原型上的属性
可以使用 hasOwnProperty
过滤判断对象本身的属性
function toVal(mix) {
var k, y, str='';
// 传入 string、number 类型 class 拼接,返回
if (typeof mix === 'string' || typeof mix === 'number') {
str += mix;
} else if (typeof mix === 'object') {
// 数组,则遍历处理,只处理子项为 true 值,处理完成后返回
if (Array.isArray(mix)) {
for (k=0; k < mix.length; k++) {
if (mix[k]) {
// 数组的子项是嵌套对象或数组时嵌套遍历处理
if (y = toVal(mix[k])) {
str && (str += ' ');
str += y;
}
}
}
} else {
// object,遍历对象的 k,k 索引的值为 true,将 k 进行 class 拼接,完成后返回
for (k in mix) {
if (mix[k]) {
str && (str += ' ');
str += k;
}
}
}
}
return str;
}
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
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
# 关于依赖库 obj-str
class 拼接的关键代码参考了obj-str (opens new window)
obj-str
只实现了对象类型的 class 拼接
export default function (obj) {
var k, cls='';
for (k in obj) {
if (obj[k]) {
cls && (cls += ' ');
cls += k;
}
}
return cls;
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 小结
处理 html 中 class 拼接的核心在于将多个字符串使用空格分隔
可以将结果放到一个数组里面,使用 join(' ')
方法处理
但更推荐 clsx
的处理方式,足够小巧
对于现有库的改进提升性能或拥有自己的亮点,是一种不错的主意 🎉
扫一扫,微信中打开