classnames的基础用法归集
- classnames这个插件在react项目业务中用的相对来说是比较多的,那这个插件是用来干什么的呢,它是用来合并类名的,存在多个类名变量时,想添加到对应的元素中,是可以采用类名,这种插件在react相关业务使用比较多,因为react代码的业务场景,非常适合变量和元素的绑定,而classnames很好迎合react的相关的业务场景。那我们先来看classnames相关用法吧
- 基础用法
- classnames这个可以是两个参数以上,类型可以字符串或者对象,如果是字符串,自动合并成多个类名,如果是对象呢?
-
classnames({a:true}, {b:true})
只有a和b属性值是布尔值,只有为true时,类才会显示成功 -
它还能通过es6中的模板字符串去绑定类名:
let buttonType = 'primary';classNames({ [
btn-${buttonType}]: true };
-
与react相关组件,基础用法:
var classNames = require('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/bind
去实现类名对象合并
import { Component } from 'react'; import classNames from 'classnames/bind'; import styles from './submit-button.css'; let cx = classNames.bind(styles); export default class SubmitButton extends Component { render () { let text = this.props.store.submissionInProgress ? 'Processing...' : 'Submit'; let className = cx({ base: true, inProgress: this.props.store.submissionInProgress, error: this.props.store.errorOccurred, disabled: this.props.form.valid, }); return <button className={className}>{text}</button>; } };
- 可以通过
classnames的源码实现
- 先将
classnames
源码进行clone下来分析一下它的结构,第一步先找package.json
文件,关键要看这几个属性,main,types,devDependencies,Dependencies,scripts
这几大属性 - main属性源码包的入口,业务中通过require/import引入时的主入口文件,scripts里面配置了可能存在调试源码相关命令,devDependencies,Dependencies代表插件中使用了哪些依赖包
- 通过main中看到index.js文件中,通过源码包中分析得知,
index.js
是源码的code,不是编译后的code - 接下来我们主要分析index.js中的源码,在这个源码中只有50行左右非常精简啦
- 采用了自执行的函数,避免变量污染冲突;采用严格模式
- 它的源码方法是classnames这个方法,先定一义一个
var classes = []
数组对象,后遍历arguments参数对象,先把不存在的参数或者参数类型false/undefined/null
给过滤掉,接下来进行判断类型,string/number
类型,直接装到数组中,判断数组进行递归,判断纯对象字面量,就遍历对象,将对象的key值是否存在,存在装到classes
数组对象中,不是存对象字面量,转换成string类型装到数组中,这是classnames方法实现的一个思路,下面附一源码
function classNames() {
var classes = [];
for (var i = 0; i < arguments.length; i++) {
var arg = arguments[i];
if (!arg) continue;
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) { // 【object object】
for (var key in arg) {
if (hasOwn.call(arg, key) && arg[key]) {
classes.push(key);
}
}
} else {
classes.push(arg.toString());
}
}
}
return classes.join(' ');
}
- 代码还做了一个引入环境的判断,
commonjs/amd/window
模块环境引入,其实这就是一个umd一个的写法哟
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;
}
- 当然还可以采用打包工具直接去打包,也就没必要去写这个模块的环境的代码咯。。。
- 上面我们将classnames函数分析了一遍,接下我们bind.js文件中的源码就是引入一个this的使用,其他的源码逻辑几乎一致
- dedupe.js这个文件中对源码再进行一步修改,进行封装,采用了类多态的一个实现思路
- 其他工具包,通过查看package.json,看到两个比较陌生的包的
benchmark
和browserify
,我们先看这两个包是用来干嘛的吧。
- 从官方文档,我们得知,benchmark用来做基准性能测试的,如果想要了解怎么去做基准性能测试,请点击, 看博客,感兴趣请自行研究
browserify
从博客中,它是一款前端基于浏览器的打包工具,这款工具年代十足,此地不过多赘述了,感兴者,自行研究。
总结
- 从文档中熟悉了classnames的基础用法,这是一款与react项目结合比较紧密的插件
- 从dedupe.js中学习了多态的实现方案
- 还了解了
benchmark
是一款基准性能测试工具,还知道了browserify
是款基于浏览器端的打包工具