1. 介绍
classnames是一个简单的JS库,可以非常方便的通过条件动态的控制class类名的显示
ClassNames是一个用于有条件处理classname字符串连接的库
简单来说就是动态地去操作类名,把符合条件的类名粘在一起
现在的问题:字符串的拼接方式不够直观,也容易出错
2. 安装
npm install classnames
3. 引入
在nodejs里引入
var classNames = require('classnames');
在js里引入
import classnames from 'classnames'
基本使用
普通字符串粘合
将参数拼接为字符串,中间用空格分开
classNames('foo', 'bar'); // => 'foo bar'
带条件的类参数
这里第二个参数是对象类型,键值为true,则粘合进classname里
classNames('foo', { bar: true }); // => 'foo bar'
若为false,则不粘进去
classNames('foo', { bar: false }); // => 'foo'
参数类型是数组
var arr = ['b', { c: true, d: false }];
classNames('a', arr); // => 'a b c'
特别注意
null和undefiend会被忽略
classNames(null, false, 'bar', undefined, 0, 1, { baz: null }, ''); // => 'bar 1'
在react中优雅地使用classnames
下面这段代码,通过if-else判断state的状态,动态选择btnClass的具体值
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
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>;}
}
我的使用
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源码:
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方法,而且还是自定义的
References