classnames的理解

发布时间 2023-12-07 14:41:00作者: 前端加油站

本篇文章主要是学习classnames的相关理解及使用。
下面列举的是如何在项目中使用

安装方式

npm install classnames

使用方式

  1. 引入时可使用require的方式引入,也可以通过import的方式引入

使用方法

import classnames form 'classnames
classnames('foo','bar') == 'foo bar'
classnames('foo',{bar:true}) == 'foo bar'

一般在React中,我们会在构建组件或需要根据某些判断条件动态生成类名时,classnames会非常有用。

示例一:

import React, { useState } from 'react';

export default function Button (props) {
	const [isPressed, setIsPressed] = useState(false);
	const [isHovered, setIsHovered] = useState(false);

	let btnClass = 'btn';
	if (isPressed) btnClass += ' btn-pressed';
	else if (isHovered) btnClass += ' btn-over';

	return (
		<button
			className={btnClass}
			onMouseDown={() => setIsPressed(true)}
			onMouseUp={() => setIsPressed(false)}
			onMouseEnter={() => setIsHovered(true)}
			onMouseLeave={() => setIsHovered(false)}
		>
			{props.label}
		</button>
	);
}

使用classnames之后

import React, { useState } from 'react';
import classNames from 'classnames';

export default function Button (props) {
	const [isPressed, setIsPressed] = useState(false);
	const [isHovered, setIsHovered] = useState(false);

	const btnClass = classNames({
		btn: true,
		'btn-pressed': isPressed,
		'btn-over': !isPressed && isHovered,
	});

	return (
		<button
			className={btnClass}
			onMouseDown={() => setIsPressed(true)}
			onMouseUp={() => setIsPressed(false)}
			onMouseEnter={() => setIsHovered(true)}
			onMouseLeave={() => setIsHovered(false)}
		>
			{props.label}
		</button>
	);
}

源码理解

classnames文件目录结构
classnames
┣ ?benchmarks
┃ ┣ ?fixtures.js
┃ ┣ ?run.js
┃ ┣ ?runChecks.js
┃ ┣ ?runInBrowser.js
┃ ┣ ?runSuite.js
┣ ?tests
┃ ┣ ?bind.js
┃ ┣ ?dedupe.js
┃ ┣ ?index.js
┣ ?bind.js
┣ ?dedupe.js
┗ ?index.js

1. 基础功能
  1. index.js中代码并不多,在这个文件中定义了一个数组classnames,一个包含所有最终有效的class的数组
    var classes = []
  2. for循环函数自带arguments实例,得到classnames函数的所有实参。当某个实参不存在时 跳过进入下一次迭代
for(var i = 0;i<arguments.lengthl;i++){
		var arg = arguments[i];
		if (!arg) continue;
}
  1. 获得每个参数的数据类型,后面根据不同的数据类型做相应的处理;如果参数是字符串或数值,直接放到classes数组中;如果参数的数据类型是对象,处理方式如下
	var argType = typeof arg;
	if (argType === 'string' || argType === 'number') {
		classes.push(arg);
	} else if (Array.isArray(arg)) {
		if (arg.length) {
		var inner = classNames.apply(null, arg);
			if (inner) {
				classes.push(inner);
			}
		}
	} else if (argType === 'object') {
		if (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {
			classes.push(arg.toString());
			continue;
		}
		for (var key in arg) {
			if (hasOwn.call(arg, key) && arg[key]) {
				classes.push(key);
			}
		}
}
  1. 提供了三种导出方式
	if (typeof module !== 'undefined' && module.exports) {
		classNames.default = classNames;
		module.exports = classNames;
	} else if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {
		// register as 'classnames', consistent with npm package name
		define('classnames', [], function () {
			return classNames;
		});
	} else {
		window.classNames = classNames;
	}
2.bind功能

在官方的文档中,bind版本给出的是在使用 css-modules 方式,或者抽象类和实际输出到DOM的真实类做映射的方式,可能需要使用bind变量

const classNames = require('classnames/bind');

const styles = {
	foo: 'abc',
	bar: 'def',
	baz: 'xyz',
};

const cx = classNames.bind(styles);

const className = cx('foo', ['bar'], { baz: true }); // => 'abc def xyz'
3. dedupe功能

dedupe的主要功能是帮助消除类的重复数据,确保结果集中已经排除后面参数中指定的错误类。
相比于bind功能,dedupe做的处理更多一些,

  1. 使用Object.create()创建一个新的对象,可以帮助后面跳过hasOwnProperty检查。
	function StorageObject() {}
	StorageObject.prototype = Object.create(null);
  1. 利用对象的key不会重复的特点去重。
	function _parseString (resultSet, str) {
		var array = str.split(SPACE);
		var length = array.length;
		for (var i = 0; i < length; ++i) {
			resultSet[array[i]] = true;
		}
	}
  1. 定义一个数组,遍历所有value值,将返回结果为true的放到list中