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": "luke.edwards05@gmail.com","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 mapif (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 filewrite(output.import.default, input);// transform ESM -> CJS exportswrite(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 exportsinput = 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