classnames用法和原码

130 阅读2分钟

源码第一句介绍

A simple JavaScript utility for conditionally joining classNames together.

一个简单的、有条件的绑定多个className的JavaScript实用函数

用法

classnames函数支持多个传参,支持数值、字符串、对象、数组等

classnames(class1, class2, ...classN)

基本用法

// 多个字符串 两种写法均可
classNames('mt10', 'font20') // => 'mt10 font20'
classNames('mt10 font20') // => 'mt10 font20'
 
//「字符串+对象」组合
classNames('switch', { selected: false }) // => 'switch'
classNames('switch', { selected: true }) // => 'switch selected'
 
// 对象 三种写法均可
classNames({ switch: true }, { selected: true }); // => 'switch selected'
classNames({ switch: true, selected: true }); // => 'switch selected'
classNames({ 'switch selected': true }); // => 'switch selected'
 
// 数组
classNames('font20', ['mt10', { switch: true, selected: false }]); // => 'font20 mt10 switch'

ES6语法

支持变量

const disabled = 'disabled';
classNames('radio', { [`radio-${disabled}`]: true }); // => 'radio radio-disabled'
classNames('radio', `radio-${disabled}`); // => 'radio radio-disabled'

结合React和CSSmodule

在React中,class写为className

import classNames from 'classnames';
import Style from './index.less'
 
const isHorizontal = true
const checkboxCls = classNames(
'global-style', 
Style.checkboxGroup, 
{ [Style.checkboxGrouphHorizontal]: isHorizontal },
);
<div className={checkboxCls}></div>

小结

classnames支持多种写法

classnames函数的入参是无序的,但是编写习惯是字符串类型的放前面,其他放后面

classnames函数会忽略入参中的错误值

classnames函数支持动态类名

原理

核心目标:

对不同类型入参进行不同处理,统一转换为为字符串,最终返回一个拼接的字符串

实现自己的classnames

function classNames (...args) {
  let classes = ''
  // 遍历解析每个入参,拼接到类字符串中
  args.forEach(arg => {
    if(arg) classes = appendClass(classes, parseValue(arg))
  })
  return classes
}
// 解析某个值为字符串
function parseValue(value) {
  // 如果是字符类型直接返回该字符串
  if (typeof value === 'string') return value
  // 如果不是对象类型返回空字符
  if (typeof value !== 'object') return ''
  // 如果是数组,apply扁平化数组递归classNames返回类名字符串
  if (Array.isArray(value)) {
   return classNames.apply(null, value)
  }
  // 如果对象自定义了toString方法
  if(value.toString !== Object.prototype.toString) return value.toString()
 
  let classes = ''
  // 一般对象 拼接自定义属性且值为true的key到类名字符串
  Object.keys(value).forEach(key => {
    if (value[key]) classes = appendClass(classes, key) 
  })
 
  return classes
}
// 拼接字符串
function appendClass(value, newValue) {
  if(!newValue) return value
  // 如果是首次则直接返回新字符串,否则返回拼接结果
  return value ? `${value} ${newValue}` : newValue
}

拓展

深拷贝实现,也是根据不同数据类型采取不同的拷贝方式。