ClassNames库详解
简述
ClassNames是一个用于有条件处理classname字符串连接的库,非常好用~。
简单来说就是动态地去操作类名,把符合条件的类名粘在一起。
安装(使用npm)
npm install classnames
引入
在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>; } }
原理
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
https://www.npmjs.com/package/classnames?activeTab=readme
http://www.npmdoc.org/classnameszhongwenwendangclassnames-jszhongwenjiaochengjiexi.html
https://juejin.cn/post/7178760381324591163
https://www.cnblogs.com/xiaohuochai/p/5557387.html