React classnames原理及测试用例
codeniu 人气:0前言
本期的源码阅读任务是:
- 学会 classnames 的用法
- 学会 classnames 的原理
- 测试用例的使用
classnames 的用法
Classname 是一个 JavaScript 库,它允许您有条件地将类名连接在一起。在构建 React 组件或需要根据某些条件动态生成类名时,它可能非常有用。
下面是一个如何使用 classnames
的例子:
import classnames from 'classnames'; const Button = ({ primary, size }) => { const classes = classnames('btn', { 'btn-primary': primary, 'btn-large': size === 'large', 'btn-small': size === 'small', }); return <button className={classes}>Click me</button>; };
在上面的示例中,classnames
函数接受一个类名和一个将类名映射为布尔值的对象。如果给定类名的布尔值为 true,则该类名将包含在类名的最终列表中。如果值为 false,则不包括类名。
还可以将一个字符串作为第二个参数传递给类名,在这种情况下,如果值为 true,那么它将被添加到类名的最终列表中。
const classes = classnames('btn', primary && 'btn-primary');
学会 classnames 的原理
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 && !arg.toString.toString().includes("[native code]") ) { classes.push(arg.toString()); continue; } for (var key in arg) { if (hasOwn.call(arg, key) && arg[key]) { classes.push(key); } } } } return classes.join(" "); }
主要工作原理如下:
- 函数声明了一个名为
class
的空数组,该数组将用于存储生成的类名。 - 然后,函数进入一个循环,循环遍历传递给函数的参数。对于每个参数,执行以下步骤:
- 检查参数的类型。如果它是一个字符串或数字,添加到类数组。
- 如果参数是数组,则函数检查它是否为非空。如果是,则函数以数组元素作为参数递归地调用自身,并将结果添加到类数组中。
- 如果参数是一个对象,那么函数将检查它是否有一个
toString
方法,该方法不是本机Object.Prototype.toString
方法。如果是这样,调用oString
的结果将添加到类数组中。如果不是,函数将遍历对象自己的可枚举属性,并将属性名称添加到类数组中(如果它们的对应值为真)。 - 循环结束后,将类数组合并到一个单独的字符串中,使用一个空格字符作为分隔符,并返回结果字符串。
测试用例的使用
className
库使用 Mocha
进行代码测试:
Mocha
是一个运行在 Node.js 和浏览器上的 JavaScript 测试框架。它用于编写和运行 JavaScript 代码的测试用例。
使用 Mocha
写测试用例的简单示例:
const assert = require("assert"); describe("myFunction", () => { it("should return the expected result", () => { assert.equal(myFunction(1, 2), 3); }); });
- it 函数用于定义单个测试用例。
- 断言模块用于验证函数是否返回预期的结果。
一些测试用例
describe('classNames', function () { // 测试能够识别具有真值得对象 it('keeps object keys with truthy values', function () { assert.equal(classNames({ a: true, b: false, c: 0, d: null, e: undefined, f: 1 }), 'a f'); }); // 检查 classNames 函数是否正确地处理了其输入参数中的假值,并且只在生成的类名字符串中包含真值。 it('joins arrays of class names and ignore falsy values', function () { assert.equal(classNames('a', 0, null, undefined, true, 1, 'b'), 'a 1 b'); }); // 这个测试用例检查 classNames 函数是否正确地处理了各种不同类型的参数 it('supports heterogenous arguments', function () { assert.equal(classNames({a: true}, 'b', 0), 'a b'); }); // 这个测试用例检查 classNames 函数是否正确地从生成的类名字符串中删除了前导空格和尾随空格。 it('should be trimmed', function () { assert.equal(classNames('', 'b', {}, ''), 'b'); }); // 这个测试用例检查 classNames 函数在调用时是否返回一个空字符串,该函数的唯一参数是一个空对象。 it('returns an empty string for an empty configuration', function () { assert.equal(classNames({}), ''); }); // ... 省略部分测试用例 });
总结
Classname 非常有用,它能够根据应用程序的状态构建动态类名。避免编写冗长和重复的 if-else 语句来构建类名。同时,源码测试用例写得非常详尽,很有借鉴意义,可以用来参考给自己的代码写一些测试用例。
加载全部内容