classnames源码阅读

98 阅读2分钟

前言

classnames 是一个JavaScript工具库,用于 有条件地 将不同的class类名组合在一起

用法

classNames('foo', 'bar'); // => 'foo bar'

classNames('foo', { bar: true }); // => 'foo bar'

classNames({ 'foo-bar': true }); // => 'foo-bar'

classNames({ 'foo-bar': false }); // => ''

classNames({ foo: true }, { bar: true }); // => 'foo bar'

classNames({ foo: true, bar: true }); // => 'foo bar'

// 多种不同类型的参数
classNames('foo', { bar: true, duck: false }, 'baz', { quux: true }); // => 'foo bar baz quux'

// 假值对应的类名都会被忽略
classNames(null, false, 'bar', undefined, 0, { baz: null }, ''); // => 'bar'

// 支持动态类名
let buttonType = 'primary';
classNames({ [`btn-${buttonType}`]: true });

// 在React中的使用:将classNames赋值给元素的className属性即可
export default function App() {

    return (
        <div className={classNames(...)}></div>
    )
} 

源码解析

// 获取对象的hasOwnProperty方法,该方法用于判断某个属性是否是对象本身的,而不是继承自原型链
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 '';
	}
    // 如果是数组,调用classNames来返回一个组合好的结果字符串
	if (Array.isArray(arg)) {
		return classNames.apply(null, arg);
	}
    // arg.toString !== Object.prototype.toString 表示arg的toString方法不是继承自Object上
	/**
	 *   !arg.toString.toString().includes('[native code]')
	 *   如果arg有toString方法,进一步检查这个方法是否是原生的
	 *   方法是将 arg.toString 方法转换成字符串,然后检查它是否包含字符串 '[native code]'。如果一个函数的字符串表示中包含 '[native code]',这通常意味着函数是原生提供的,而不是用户定义的。
	 */
	if (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {
		return arg.toString();
	}

	let classes = '';

	// 如果arg是对象
	for (const key in arg) {
		// hasOwn 判断key是否是arg对象本身的属性,而不是继承自原型链的属性
		// arg[key] 判断key对应的值是否存在,且不为false
		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;
}

最后

读源码,相当于和大佬对话,给了我许多的收获。

如果对我的笔记感兴趣,也可以前往我的个人博客,里面有更多我的学习分享