背景介绍
距离上次的button调研过去了整整两周,偷懒许久,心生愧疚。
期间碍于金铲铲上大师的大业未成,加上重感冒的侵袭,一直没有时间动笔,今天感冒初愈。决定写一篇input的调研冲冲喜,也可以作为晚上上大师的祭品。
历史文章
- react组件库调研之Button篇 juejin.cn/post/702897…
- input焦点跳动问题探索 juejin.cn/post/703099…
调研ing
还是参照上一篇的方法,依次从ui对比 功能对比 代码分析三个方面,可能会加上代码风格对比。
调研的库也是老朋友,ant arco semi zent。
ui对比
说实话,在input的ui上,感觉并没有特别的地方。个人比较是喜欢ant的样式。
不知道为啥,semi的大哥,要把背景色整成灰色的。
而arco,没有focus时候是灰色,focus以后就变白了。
不过,众所周知,前端只是设计师的工具人罢了。
功能对比
| 框架 \ 功能 | 前缀后缀 | clearable | password | search | textarea | 自动拓展的多行输入框 |
|---|---|---|---|---|---|---|
| ant | 有 | 有 | 有 | 有 | 有 | 有 |
| semi | 有 | 有 | 有 | 有 | 有 | 有 |
| arco | 有 | 有 | 有 | 有 | 有 | 有 |
| zent | 有 | 有 | 开发ing | × | 有 | × |
在功能上,大哥们出奇得统一,只有我们的zent,惜败一筹,只能说,后续尽量补上。
代码风格对比
只有arco使用了hook,semi和ant还都是class写法。
代码分析
这次就选了clear和自动拓展的多行输入框这2个功能来进行分析。本来想选password,但是在上一篇,把password中的功能单拎了出现写了,有兴趣的大哥,可以看看之前的。
clear输入框
arco和ant的表现形式都是点击clear图标,重新focus。而semi的表现形式是点击clear后,input失去焦点,在实际场景中,我们大概率不需要失焦,因此,主要分析ant和arco的代码。
先看ant的clear图标的代码。
handleReset = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
this.setValue('', () => {
this.focus();
});
resolveOnChange(this.input, e, this.props.onChange);
};
<CloseCircleFilled
onClick={handleReset}
// Do not trigger onBlur when clear input
// https://github.com/ant-design/ant-design/issues/31200
onMouseDown={e => e.preventDefault()}
className={classNames(
{
[`${className}-hidden`]: !needClear,
[`${className}-has-suffix`]: !!suffix,
},
className,
)}
role="button"
/>
分两种情况,一种是input在focus状态下,点击clear图标,通过阻止mousedown的默认事件,来阻止失焦,防止重新触发onFocus事件。
另一种情况,在没有focus的状态下,点击clear图标,手动触发focus来使input聚焦。
接下来看看arco的代码。
<IconHover className={`${prefixCls}-clear-icon`}>
<IconClose
onClick={(e) => {
e.stopPropagation();
if (refInput.current && refInput.current.focus) {
refInput.current.focus();
}
onValueChange && onValueChange('', e);
onClear && onClear();
}}
// keep focus status
onMouseDown={(e) => {
e.preventDefault();
}}
/>
</IconHover>
代码逻辑和ant一致,使用e.preventDefault()阻止失焦。在没有focus的状态下,调用current.focus聚焦。
不过顺便发现了arco中的一个bug。
clear事件绑定在icon上,而外面的一圈iconHover上并没有绑定clear事件。因此在点击icon和iconHover中间的一段时,clear事件并没有生效,input没有清空。有兴趣的大哥,可以试一下,然后去提个issue。
自动拓展的多行输入框(内容高度自适应)
看了3个框架的解决方案,基本一致。都是用了github.com/andreypopp/… 的方案。
首先,生成一个mirror-textarea,设置上不可见的style,然后放置到body中。
const HIDDEN_TEXTAREA_STYLE = {
'min-height': '0',
'max-height': 'none',
height: '0',
visibility: 'hidden',
overflow: 'hidden',
position: 'absolute',
'z-index': '-1000',
top: '0',
right: '0',
};
再把会影响高度的style设置进mirror-textarea中。
const SIZING_STYLE = [
'borderBottomWidth',
'borderLeftWidth',
'borderRightWidth',
'borderTopWidth',
'boxSizing',
'fontFamily',
'fontSize',
'fontStyle',
'fontWeight',
'letterSpacing',
'lineHeight',
'paddingBottom',
'paddingLeft',
'paddingRight',
'paddingTop',
// non-standard
'tabSize',
'textIndent',
// non-standard
'textRendering',
'textTransform',
'width',
];
Object.keys(sizingStyle).forEach(key => {
hiddenTextarea.style[key] = sizingStyle[key];
});
然后,把value置入mirror-textarea中,因为元素的height是0,所以只需读取scrollHeight就是我们所需要的高度。
hiddenTextarea.value = value;
let height = getContentHeight(hiddenTextarea, sizingData);
总结
平心而论,学习input组件的这些日子,收获还是挺大的。
无论是mousedown mouseup和focus之间的联系,还是mirror-textarea的解决方案。都令我有一种茅塞顿开的感觉。
希望在之后的阅读中,能够学到更多。
从写开头到总结,又过去了几天,大师没上,感冒却愈发严重了,祝自己感冒早日康复,争取下一篇select调研篇早日写完。