背景介绍
很荣幸,我们小组接到了内部react组件库zent的改版需求,为了写出更好的组件,更优秀的代码,对现有优秀组件库的调研,肯定是必不可少的。
虽然任务里说的是竞品调研,但以我们的使用体量,应该根本算不上竞品,毕竟连我在接到任务前根本没听说过zent,只知道vant。说到vant,有赞前端中台招人,来了就可以和被尤大大夸赞过的vant作者chenjiahan大哥,一起上山点菜,一起打王者,还能给你打辅助,直接留言或者联系我都行!
言归正传,首先感谢ant,给了我最初调研的素材。其次,感谢字节的大佬们,雪中送炭开源了semi和arco。
我分到的基本都是form组件的开发和修改,第一篇,就先从button开始了。
调研ing
大部分的篇幅肯定都是介绍大佬们开发的3个库的,毕竟要抱着学习的心态,绝对不是因为怕吐槽zent被发现,被大哥们按在地上打。
ui对比
个人见解:semi > ant > arco = ??
semi
ant
arco
??
功能对比
| 框架 \ 功能 | text按钮 | icon按钮 | 链接按钮 | 禁用加载 | 组合按钮 | 下拉按钮组 |
|---|---|---|---|---|---|---|
| ant | 有 | 有 | 有 | 有 | 有 | × |
| semi | 有 | 有 | × | 有 | 有 | 有 |
| arco | 有 | 有 | 有 | 有 | 有 | × |
| zent | 开发ing | 修改ing | 有 | 有 | 有 | 准备开发ing |
在功能上,基本都是该有的都有,ant和arco基本是持平的,ant多了个dashed边框,loading支持传delay控制时间,而semi虽然少了链接按钮功能,但是文档里也写了,推荐用link属性来实现,而且多了一个下拉按钮组的功能,下面放图了,且semi的icon支持通过iconPosition控制位置,而ant和arco需要手动写到button里。
代码风格对比
我可太喜欢semi的代码风格了,截图截不全,就隐藏了几个对象,截了个图,清晰易懂,虽然把iconButton和button分成了2个组件,但是这个代码确实我喜欢的写法和风格(清晰到连我都看得懂)。
而且,semi跟ant的代码,在button中可以说是毫不相关,借鉴什么的根本不存在。不过有趣的事情还是出现了,只上图,不说话。
代码分析
首先,因为button算是一个比较基础简单的组件,大多数的功能其实都是依靠props配合css和class来实现的。
截图里的是semi中的代码,根据传入props不同的值,来实现按钮不同的样式。按钮中能分析的点,其实就是icon中的逻辑和2个中文字之间自动加空格。
icon处理
icon的处理简单来说,就是确定icon的位置然后把children塞到icon的左边或者右边。
semi的做法是不处理children,直接在children外面整个套个span,然后根据iconPosition判断margin-left/right,在span上直接套style。
ant和arco的处理就相对复杂一点,就是上面截图的雷同的那一段代码,拿arco的举例。
function processChildren(children?: ReactNode) {
const childrenList = [];
let isPrevChildPure = false;
React.Children.forEach(children, (child) => {
const isCurrentChildPure = typeof child === 'string' || typeof child === 'number';
if (isCurrentChildPure && isPrevChildPure) {
const lastIndex = childrenList.length - 1;
const lastChild = childrenList[lastIndex];
childrenList[lastIndex] = `${lastChild}${child}`;
} else {
childrenList.push(child);
}
isPrevChildPure = isCurrentChildPure;
});
return React.Children.map(childrenList, (child) =>
typeof child === 'string' ? <span>{child}</span> : child
);
}
其实就是把string和number类型的child整合到了一起,放进了一个span里,然后再从css入手,给svg和span中加个margin。
如果不是string的内容,也就不会产生margin了,随便整个a标签,因为逻辑没走到加span这步,也就不会有margin了。
2个汉字间自动加空格
这个功能描述起来不是很通俗易懂,上个图就明白了。semi没有提供这个功能,arco和ant在configProvider配一下,就能使用了,ant是默认开的。描述一下,就是如果按钮的child是两个汉字的话,就自动在中间加个空格,美观一点。
判断是否是2个中文,只需要正则判断一下就行了。
const rxTwoCNChar = /^[\u4e00-\u9fa5]{2}$/;
const isTwoCNChar = rxTwoCNChar.test.bind(rxTwoCNChar);
虽然是一个功能,但是ant和semi实现的方案也不一样。
ant中,当只有一个child时,spaceChildren之后,ant会把string形式的child,手动添加一个空格。
if (typeof child === 'string') {
return isTwoCNChar(child) ? <span>{child.split('').join(SPACE)}</span> : <span>{child}</span>;
}
但在arco中,则是采用了css中的letter-spacing的方式来完成这个样式。
综合评判一下,我感觉这波应该是arco赢了,在ant中,如果我添加2个都是一个汉字的child后,并不会自动添加空格,因为在React.Children.count(children) === 1这个逻辑下就挂了,而arco因为直接是css控制,不会受到影响。
const a = '确';
const b = '定';
<Button type="primary">{a}{b}</Button>
当然可能也是我没有考虑到另外一些点,如果说错了,也希望大佬可以纠正。
动画
动画这个只有ant做了,在点击ant的按钮时,会发现周围会有波浪形状的阴影波动,这个截图太难了,大家可以自己点一点,因为ant在button外面又套了一层的wave的动画组件。大佬果然牛逼,简简单单地按钮也安排得明明白白。
感兴趣的大哥,可以去看一看,动画的事情,无非就是点一点加个class,然后写个animation罢了。
虽然我不会,但是我会吹牛。
总结
其实最先看的不是button的,而是input的,但是因为button的组件相对来说比较简单,可以边写边练手,input有些过于复杂了,直接来写应该是一坨屎。
虽然button组件相对简单,但是也从大佬们的代码中学到了很多,受益匪浅。
button篇写的还算顺畅,下一篇input边写边改边学,也不知道要多久才能熬出来~