React classNames 模块

classNames

classNames

今天分享一个好玩的东西,它叫 classNamesopen in new window ,这个是一个翻译的版本,原版链接在这里open in new window它是一个小工具。我是在 React 项目里面用到它的。一用就停不下来了

它是什么?

它是一个简单的对类名进行条件判断并且拼装的小工具。通过条件判断把对应的 class 串接起来,可以更清晰的展示组件的状态对应的 css.
总的来说,就是让你的代码可读性更高

它用在哪里?

React 官方推荐用它,当然,既然是小工具,那么肯定扩展性是非常强的。不仅仅是 React!!不仅仅是 React!!不仅仅是 React!!重要的事情我们说三遍

为什么要用它?

项目 CSS 代码的规范遵循的是BEMopen in new window,所以我们有很多的类名是有状态定义的,比如 .sidebar__item--active 。你应该可以一眼看出来,这是一个侧边导航栏的激活状态,在选中的时候显示。但是通常还有一个默认状态 .sidebar__item ,那么这个时候,你通常会加多一个判断语句,因为你需要判断是不是激活的。

侧边栏激活状态

var SideBar = React.createClass({
  // ...
  render() {
    var sideBarClass = 'sidebar__item'
    if (this.state.isActive) {
      sideBarClass += '--active'
    }
    return <div className={sideBarClass}>{this.props.text}</div>
  }
})

那么假设你还有一个自定义颜色的 .sidebar__item-orange 类。
那你的代码会多一个 if 判断.

var SideBar = React.createClass({
  // ...
  render() {
    var sideBarClass = 'sidebar__item'
    if (this.state.isActive) {
      sideBarClass += '--active'
    }
    if (this.props.color) {
      sideBarClass += '' + this.props.color //'+'优先于'+='
    }
    return <div className={sideBarClass}>{this.props.text}</div>
  }
})

假如……好了没那么多假如了,你的类名有多长,你的 if 就会有多长。
所以作为一个有追求的前端,不能忍!!

不能忍

怎么用?

还是上面的例子

var cl = require('classnames');
var SideBar = React.createClass({
  // ...
  render () {
    var sideBarClass = cl({
        'sidebar__item': true,
        '--active': this.state.isActive,
        [this.props.color] :this.props.color, //这里用的是ES6的语法,可以用JSX编译工具如babel转换出来
        ....
        ....
        ....
    });
    return <div className={sideBarClass}>{this.props.text}</div>;
  }
});

有多少可以来多少,不怂好吗。
引用了一个模块,我们的代码就优雅了好多了,可能你还会担心,这模块大不大啊,引入的话会不会得不偿失啊。我们看一下源代码吧。

;(function() {
  var hasOwn = {}.hasOwnProperty
  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)) {
        classes.push(classNames.apply(null, arg))
      } else if (argType === 'object') {
        for (var key in arg) {
          if (hasOwn.call(arg, key) && arg[key]) {
            classes.push(key)
          }
        }
      }
    }
    return classes.join(' ')
  }

  //CommonJS规范
  if (typeof module !== 'undefined' && module.exports) {
    module.exports = classNames

    //AMD规范
  } 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
  }
})()

代码真心短得可怜,而且还可以再 DIY,最后三个if,其实你可以选择你项目用的规范来引入。默认支持 CommonJS 规范/AMD 规范/全局定义 三种。

我们再观察一下原理部分,看有没有什么 bug。知己知彼才能放心插入

一个内部数组classes,然后对参数进行判断后将参数添加到内部数组里
参数类型判断条件

  • string 或者 number 类型直接 push 到数组
  • Array 类型则递归这个数组
  • Object 类型则把键为 true 的加入数组

最后join了数组返回一个字符串。代码很简单,但是创意无限。
非常推荐大家用这个模块。有问题可以留言哈。

Last Updated:
<manfred>峯</hu>
欢迎关注微信公众号 【Big前端】无广告,无软文,就是这么傲娇。直推一线大厂高质量内容,不局限于前端·后台·运维相关,还包括房价🏠、信用卡💳等内容也可内推一线大厂腾讯阿里字节,对腾讯字节比较熟悉,简历可以发给我,我会给你介绍一线大厂的情况,让你更加了解一线大厂