clsx源码
版本 2.1.1
一个处理类名的函数。
地址:https://github.com/lukeed/clsx/blob/master/src/index.js
源码文件index.js
文件 src/index.js
//src/index.js
function toVal(mix) {
var k,
y,
str = "";
// 判断参数min类型, 字符串或数值类型 直接 拼接
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++) {
// 为真 才递归调用toVal函数处理
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;
源码文件lite.js
只处理字符串。
//src/lite.js
export function clsx() {
// 定义声明变量
var i = 0,
tmp,
str = "",
len = arguments.length;
// 参数遍历
for (; i < len; i++) {
if ((tmp = arguments[i])) {
// 当参数为字符串时处理,拼接字符串
if (typeof tmp === "string") {
str += (str && " ") + tmp;
}
}
}
return str;
}
export default clsx;
使用
import clsx from "clsx";
// or
import { clsx } from "clsx";
// Strings (variadic)
clsx("foo", true && "bar", "baz");
//=> 'foo bar baz'
// Objects
clsx({ foo: true, bar: false, baz: isTrue() });
//=> 'foo baz'
// Objects (variadic)
clsx({ foo: true }, { bar: false }, null, { "--foobar": "hello" });
//=> 'foo --foobar'
// Arrays
clsx(["foo", 0, false, "bar"]);
//=> 'foo bar'
// Arrays (variadic)
clsx(["foo"], ["", 0, false, "bar"], [["baz", [["hello"], "there"]]]);
//=> 'foo bar baz hello there'
// Kitchen sink (with nesting)
clsx(
"foo",
[1 && "bar", { baz: false, bat: null }, ["hello", ["world"]]],
"cya"
);
//=> 'foo bar hello world cya'
打包分析
{
"name": "clsx",
"version": "2.1.1",
"repository": "lukeed/clsx",
"description": "A tiny (239B) utility for constructing className strings conditionally.",
"module": "dist/clsx.mjs",
"unpkg": "dist/clsx.min.js",
"main": "dist/clsx.js",
"types": "clsx.d.ts",
"license": "MIT",
"exports": {
//条件加载,利用.这个别名
".": {
"import": {
"types": "./clsx.d.mts",
//default条件指定其他情况的入口(即 ES6 的入口)。
"default": "./dist/clsx.mjs"
},
"default": {
"types": "./clsx.d.ts",
"default": "./dist/clsx.js"
}
},
"./lite": {
"import": {
"types": "./clsx.d.mts",
"default": "./dist/lite.mjs"
},
"default": {
"types": "./clsx.d.ts",
"default": "./dist/lite.js"
}
}
},
"author": {
"name": "Luke Edwards",
"email": "[email protected]",
"url": "https://lukeed.com"
},
"engines": {
"node": ">=6"
},
"scripts": {
"build": "node bin",
"test": "uvu -r esm test"
},
"files": ["*.d.mts", "*.d.ts", "dist"],
"keywords": ["classes", "classname", "classnames"],
"devDependencies": {
"esm": "3.2.25",
"terser": "4.8.0",
"uvu": "0.5.4"//UVU是Node.js和浏览器的一个非常快速和轻量级的测试运行器
}
}
main属性
定义这个 package
的入口文件位置。
在不支持 ES6 的 Nodejs 中,它指向的就是CommonJs模块系统的入口位置。
在 Nodejs 支持 ES6 的版本中,只要显示定义 "type": "module"
,那么它指向的就是 ESM 模块系统的入口位置
exports属性
export
的使用:https://es6.ruanyifeng.com/#docs/module-loader#package-json-%E7%9A%84-exports-%E5%AD%97%E6%AE%B5
exports 字段可以配置不同环境对应的模块入口文件,并且当他存在时,它的优先级最高,当package.json
文件中存在 exports
字段,设置的 main
字段会失效。exports
字段的优先级高于 main
字段。
示例
利用.
这个别名,可以为 ES6
模块和 CommonJS
指定不同的入口。
{
"type": "module",
"exports": {
".": {
"require": "./main.cjs",
"default": "./main.js"
}
}
}
上面代码中,别名.
的require
条件指定require()
命令的入口文件(即 CommonJS
的入口),default
条件指定其他情况的入口(即 ES6
的入口)。
执行打包
命令
{
"scripts": {
"build": "node bin",
"test": "uvu -r esm test"
},
}
bin/index.js文件
// @ts-check
const fs = require("fs");
// zlib 模块提供了使用 Gzip、Deflate/Inflate、以及 Brotli 实现的压缩功能。
const zlib = require("zlib");
// 一个用于ES6+的JavaScript管理器/压缩器工具包。
const { minify } = require("terser");
const pkg = require("../package.json");
// file 文件的路径 , source文件的内容
function write(file, source) {
let isModule = !source.startsWith("!function");
// 一个可配置的高级函数async minify(code, options),它将以可配置的方式执行所有缩小阶段。默认情况下,minify()将启用compress和mangle。
let result = minify(source, {
module: isModule,
compress: true,
});
// console.log(result.code); // minified output: function add(n,d){return n+d}
// console.log(result.map); // source map
if (result.code) {
fs.writeFileSync(file, result.code);
// zlib.gzipSync压缩数据块 byteLength获取字节大小
let size = zlib.gzipSync(result.code).byteLength;
console.log('~> "%s" (%d b)', file, size);
} else {
console.error('!! "%s" ::', file, result.error);
}
}
// file文件名称,entry文件别名
function bundle(file, entry) {
// 判断是否存在文件夹dis,不存在则创建
fs.existsSync("dist") || fs.mkdirSync("dist");
//pkg 即package.json默认导出的对象
let output = pkg.exports[entry];
// 读取文件
let input = fs.readFileSync(file, "utf8");
// copy for ESM file
write(output.import.default, input);
// transform ESM -> CJS exports
write(
output.default.default,
input
.replace("export function", "function")
.replace(
"export default clsx;",
"module.exports = clsx;\n" + "module.exports.clsx = clsx;"
)
);
if (entry === ".") {
// transform ESM -> UMD exports
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 +
"});"
);
}
}
bundle("src/index.js", ".");
bundle("src/lite.js", "./lite");
package.josn
文件中的exports
配置信息
{
"exports": {
".": {
"import": {
"types": "./clsx.d.mts",
"default": "./dist/clsx.mjs"
},
"default": {
"types": "./clsx.d.ts",
"default": "./dist/clsx.js"
}
},
"./lite": {
"import": {
"types": "./clsx.d.mts",
"default": "./dist/lite.mjs"
},
"default": {
"types": "./clsx.d.ts",
"default": "./dist/lite.js"
}
}
}
}
npm install过程
首先安装的依赖都会存放在根目录的node_modules,默认采用扁平化的方式安装,并且排序规则.bin第一个,然后@系列,再然后按照首字母排序abcd等,并且使用的算法是广度优先遍历,在遍历依赖树时,npm会首先处理项目根目录下的依赖,然后逐层处理每个依赖包的依赖,直到所有依赖都被处理完毕。在处理每个依赖时,npm会检查该依赖的版本号是否符合依赖树中其他依赖的版本要求,如果不符合,则会尝试安装适合的版本。
扁平化的理想状态
APP————A模块(c1.0)
APP————B模块(c1.0)
安装某个二级模块时,若发现第一层级有相同名称,相同版本的模块,便直接复用那个模块
因为A模块下的C模块被安装到了第一级,这使得B模块能够复用处在同一级下;且名称,版本,均相同的C模块
扁平化的非理想状态
APP————A模块(c1.0)
APP————B模块(c2.0)
因为B和A所要求的依赖模块不同,(B下要求是v2.0的C,A下要求是v1.0的C )所以B不能像2中那样复用A下的C v1.0模块 所以如果这种情况还是会出现模块冗余的情况,他就会给B继续搞一层node_modules,就是非扁平化了。
uvu插件
UVU是Node.js和浏览器的一个非常快速和轻量级的测试运行器.
# via `uvu` cli, for all `/tests/**` files
$ uvu -r esm tests
# via `node` directly, for file isolation
$ node -r esm tests/demo.js