classnames库的实现

319 阅读1分钟

本文参加了由公众号@若川视野发起的每周源码共读活动,点击了解详情一起参与。

关于classnames

classnames库的介绍和用法可以参考GitHub地址 github.com/JedWatson/c…

下面是官网用法,将所有类名以空格分开连接在一起,并且对key值为false的不加入进去

classNames('foo', 'bar'); // => 'foo bar'
classNames('foo', { bar: true }); // => 'foo bar'
classNames({ 'foo-bar': true }); // => 'foo-bar'
classNames({ 'foo-bar': false }); // => ''
classNames({ foo: true }, { bar: true }); // => 'foo bar'
classNames({ foo: true, bar: true }); // => 'foo bar'

// lots of arguments of various types
classNames('foo', { bar: true, duck: false }, 'baz', { quux: true }); // => 'foo bar baz quux'

// other falsy values are just ignored
classNames(null, false, 'bar', undefined, 0, 1, { baz: null }, ''); // => 'bar 1'

如何实现

(function () {
        'use strict';

        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;
                // 字符串或数字直接push
                if (argType === 'string' || argType === 'number') {
                    classes.push(arg);
                    // 是数组递归调用每一项 这里用到apply传递参数
                } 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) {
                        for (var key in arg) {
                            // 是自己的属性并且值为真就push
                            if (hasOwn.call(arg, key) && arg[key]) {
                                classes.push(key);
                            }
                        }
                    // 否则就用自己的toString方法
                    } else {
                        classes.push(arg.toString())
                    }
                }
            }
            // 返回用空格隔开的结果
            return classes.join(' ');
        }
        // 支持各种导出形式
        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;
        }
    }());

总结

  • 学到了使用递归+apply将数组达到扁平化传递参数
  • 如何支持各种导出模式