1. 介绍
classnames是一个简单的JS库,可以非常方便的通过条件动态的控制class类名的显示
ClassNames是一个用于有条件处理classname字符串连接的库
简单来说就是动态地去操作类名,把符合条件的类名粘在一起
现在的问题:字符串的拼接方式不够直观,也容易出错
2. 安装
javascript
npm install classnames
3. 引入
在nodejs里引入
javascript
var classNames = require('classnames');
在js里引入
javascript
import classnames from 'classnames'
基本使用
普通字符串粘合
将参数拼接为字符串,中间用空格分开
javascript
classNames('foo', 'bar'); // => 'foo bar'
带条件的类参数
这里第二个参数是对象类型,键值为true,则粘合进classname里
javascript
classNames('foo', { bar: true }); // => 'foo bar'
若为false,则不粘进去
javascript
classNames('foo', { bar: false }); // => 'foo'
参数类型是数组
javascript
var arr = ['b', { c: true, d: false }];
classNames('a', arr); // => 'a b c'
特别注意
null和undefiend会被忽略
javascript
classNames(null, false, 'bar', undefined, 0, 1, { baz: null }, ''); // => 'bar 1'
在react中优雅地使用classnames
下面这段代码,通过if-else判断state的状态,动态选择btnClass的具体值
javascript
class Button extends React.Component {
// ...
render () {
var btnClass = 'btn';
if (this.state.isPressed) btnClass += ' btn-pressed';
else if (this.state.isHovered) btnClass += ' btn-over';
return <button className={btnClass}>{this.props.label}</button>;
}
}
现在用classnames来做,btnClass就可以边成一个对象,通过键值的条件确定最终生成的classname
javascript
import classnames from 'classnames'
class Button extends React.Component {
// ...
render () {
var btnClass = classnames({
btn: true,
'btn-pressed': this.state.isPressed,
'btn-over': !this.state.isPressed && this.state.isHovered
});
return <button className={btnClass}>{this.props.label}</button>;
}
}
我的使用
javascript
import classnames from 'classnames'
<li className="nav-sort">
{/* 高亮类名: active */}
{tabsList.map((item,index)=>
<span className={classnames('nav-item',{active:item.type===type})}
key={index} onClick={()=>{changTab(item.type)}} >{item.text}</span>)}
{/* {tabsList.map((item,index)=>
<span className={`nav-item ${item.type===type && 'active'}`}
key={index} onClick={()=>{changTab(item.type)}} >{item.text}</span>)} */}
</li>
原理
classNames源码:
javascript
function classNames() {
var classes = [];//用于存储生成的类名
for (var i = 0; i < arguments.length; i++) {//遍历classnames的所有参数
var arg = arguments[i];
if (!arg) continue;
var argType = typeof arg; //拿到每一个参数的类型
if (argType === "string" || argType === "number") { //如果是字符串或数字就直接加到classes数组里
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") { //如果是对象且有自定义的toString方法,则调用toString方法添加到classes对象里,if里面的表达式下面会详细介绍
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数组里
classes.push(key);
}
}
}
}
return classes.join(" ");// 最后在中间加上空格转成字符串
}
arg.toString !== Object.prototype.toString啥意思?
我们知道所有js对象都继承Object对象,即都继承toString方法,这个表达式的意思就是这个对象的toString方法不是继承自Object.prototype
arg.toString.toString().includes("[native code]") 啥意思?
我们首先要知道toString方法的一些知识:当我们对一个自定义函数调用toString()方法时,可以得到该函数的源代码;如果对内置函数使用toString()方法时,会得到一个'[native code]'字符串。因此,可以使用toString()方法来区分自定义函数和内置函数,注意是对函数调用toString()方法,所以我们通过对toString函数调用toString方法,就能得知这个toString是内置的,还是自定义的
这里两个表达式连起来的意思就是:现在这个类有toString方法,而且还是自定义的