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