根据产品需求迭代出来的一个简单好用的React-Input组件

1,030 阅读9分钟

这篇文章会包含如下几个小知识点:

  • 如何编写一个debounce input.
  • 如何发布一个React组件到npm.
  • 思考如何在业务中提高自己.(这纯是我在吹NB,O(∩_∩)O哈哈~)

react-best-input

这年代都讲究个专利注册,一个好听的名字真的比什么都重要,原谅我臭不要脸的把best这个名字提前占有了!真的是先下手为强啊。^_^ react-best-input经过gzip后大概在6KB左右,由于个人能力有限,代码质量应该还可以提高,陆续会改进代码质量,争取控制的更精简,而且这些功能都很容易实现,主要想说的是这个过程。下面是这个input组件的Demo,目前暂时支持如下几个功能:

  • 继承了普通input的属性,使用的时候就当成是普通的input标签增强版就可以了。
  • 标签属性label,可以自动在input前面增加label。
  • debounce特性,在特定的场景下你会想使用这个功能的,后续会说明。
  • 通过error属性以及errMsg在值不是预期的时候提示用户。
  • 自动配置hint属性,可以在下方加上input的说明介绍。
  • getLength,可以自定义配置字符的个数从而改变返回的值得真实长度,与charBase属性配套使用,默认 { 'zh-cn': 1, 'eng': 1 },默认中英文都是一个字符长度。
  • ...others,后续可能遇到新需求还会不断地加进来,比如全角半角。

下面我就记录一下我为什么想要写这个组件的心路历程:

Debounce Input

首先,我其实只是想写一个debounce input。原因有以下几点:

  • 第一、input的onChange不断变化,也就是不debounce的时候无法满足某些场景。比如:input做验证的时候,输入是否是邮箱或者手机号,那么刚输入的时候就会提示不合法,你说这不是废话吗?怎么也得等我输入完你再去判断我输入的是不是合法的邮箱啊,也就是下面的情况(写篇文章还得做动图,也是没谁了):

如上图所示,每输一次就需要校验是否是合法的url,体验很不好,如果换成debounce input的话,效果就会好很多。在代码里加上timer属性:

<BestInput
  label='类型校验'
  style={{ width: '40%' }}
  timer={600} /*上面的示例没有timer属性,这里设置用户在600ms内没其他输入再监听*/
  error={this.state.emailTrue}
  errMsg='这不是一个合法的邮箱'
  onChange={this.handleRegExp}
/>

效果如下:

可以看出来,当用户连续的输入一个合法邮箱的时候,中间是不会报错的,也就是不会进行类型检验,这就很符合一般的逻辑。

当然,不用input就不能完成上面的效果了吗?当然可以,你可以在普通的input里使用onBlur来进行输入的校验,也就是说输入完成当窗口失焦才校验,也可以实现用户输入完成进行校验的效果。但是,如果你需要动态监听input的变化,就需要使用onChange,如果你还增加校验,就需要额外写一个onBlur,而现在你只需要一个onChange就可以满足两个要求。

  • 第二、在目前的项目中,存在大量使用input的组件,并且每个input的变化需要反馈到另一个组件中。也就是使用redux,每个input框的每次onChange都会大量触发action,牺牲性能。

公司的项目就不上截图了,大家明白我的意思就好,O(∩_∩)O哈哈~。无非就是少几次不必要的请求,微微的提升一下性能~我一直觉得,即使是0.001ms的提升,也是很重要的。

  • 其他的应该还有一些优点,当然,并不是说必须debounce才好,还是要看场景,否则为什么html5不直接把debounce加入到input规范里。嗯,最终还是看自己的使用场景而定。

[实现]:实现一个debounce input其实很简单,不过在实际应用场景中,有很多都需要debounce的情况,不是只有input可以debounce,具体的可以参照lodash的debounce函数,读读源码也可根据自己的实际场景再开发。我这里就只讲怎么debounce input了。

setTimeout + clearTimeout

没错,其实就是这么简单,我们想要在用户600ms后不继续输入了再获取input的value,就是设一个600ms的setTimeout,然后在里面获取值。当用户不断输入触发setTimeout的时候,再通过clearTimeout进行重置定时器并重新计时,这样就很简单的实现了我们的需求。

// 下面是普通的js实现,可以参考一下
// 获取input元素
var textInput = document.getElementById('test-input');

// 初始化一个定时器函数
var timeout = null;

textInput.onkeyup = function (e) {
    // 不断重置定时器函数
    clearTimeout(timeout);
    // 500ms内没任何其他输入,获取debounce之后的结果
    timeout = setTimeout(function () {
        console.log('Input Value:', textInput.value);
    }, 500);
};

产品驱动业务

重点来了,作为一个刚毕业的职场小白,公司的流程还不是很熟悉,进入公司无非就是做业务。评审—>排期->开发->上线,期间可能会包含着产品多次的改需求,哈哈,我并不是针对产品哦~因此,对于我来说,我所做的是我应该做的业务,但可能并不是我想做的业务。但是,每做一个需求,我觉得还是需要思考的,为什么要这么做,为什么要改成这样,产品肯定有自己的想法,可能这么做更符合用户的逻辑等等。
废话不多说,开始进入正题,啰嗦一大堆跟这篇文章有啥关系,当然有关系了,没看到小标题吗?产品驱动业务!项目里有这么一个需求:
获取用户输入的内容,然后不能超过10个字符,但是其中中文字符算1个,英文字母和数字算0.5个
确实不是一个复杂的需求,获取输入的内容正则匹配一下然后重新计算length返回就好了,那么问题来了,如果多个组件都用到这个需求了但是要求又不一样呢?比如页面A要求中文1个英文也是1个,页面B要求中文是2个英文是1个。那岂不是每一个都要配套写对应的函数。所以,顺便的,我把这个需求也加到了react-best-input里,可以通过配置属性charBase来进行字符的修改,然后通过getLength获取自定义的输入长度。

charBase {
    'zh-cn': number//中文字符长度,
    'eng': number//英文字符长度
}
getLength(length) // 获取自定义长度

如何发布一个React组件到npm

开发完了,该发布组件到npm了,步骤特别简单,首先就是配置好webpack的各种配置,每个人配置都不一样,可以参考各种文档配置一个适合你的就可以,我就不介绍了,因为我也在进修中,O(∩_∩)O哈哈~。

这里多说一句啊,webpack4+默认就是压缩代码,也就是自带uglifyJs的功能,因此,你如果在开发的时候,可以使用webpack --mode devlopment进行打包查看打包后的代码,也可以参照我的配置。

  • 发布第一步:yarn build 把你的代码打包,为了最简化,打包完之后会在指定文件夹出现对应的文件。我的就是在lib下的index.js文件。
  • 发布第二步:yarn link 开发完了当然需要测试一下了,否则你发布的包别人安装了发现不能用岂不是很尴尬!!!使用yarn link,该命令会帮你创建本地的引用包命令,你就可以在本地使用命令来测试你的包是否正确了

这里有个小坑,本地测试必须使用双引号引用包名称,否则会报错,也就是yarn link "react-best-input"才可以。

  • 发布第三步:yarn publish 测试结束没问题了,就可以发布了,使用yarn publish命令来进行发布,当然你需要先在npm上注册自己的账号才可以,注册的过程就不多说了。第一次发布需要你的登录信息,之后就会发布你的包。

    这里也有一个小坑:npm注册的时候邮箱好像不能用国内的邮箱,要不然提示错误,没测试过,但是我用126邮箱注册了五次都没成功,而使用gmail邮箱注册了一次就成功了,相同的账户名密码。。。

  • 发布第四步:更新 更新使用的命令也是yarn publish,只不过他会提示你当前版本和你想要发布的版本,如果当前版本v1.0.1,你只需要修改一个不同的版本号就可以更新上去。

踩坑记录

react语法坑

如果没有使用原生的js编写包,使用的是react语法写的,那么在webpack的时候需要使用babel转译成es5代码才可以。需要使用babel-preset-react,babel-preset-es2015和babel-preset-stage-1。

SSR坑

服务端渲染,很火的一个词,也可能大部分项目都在用了,我是想说的不是这个问题,我想说的是如果你写的组件用到了底层诸如window,document这些变量的话,那么在服务端渲染是过不去的,一般来说是node端,node端是没有上面两个变量的。因为我用的是input,并且使用了样式,style-loader会使用底层的document.querySelector来查找dom节点插入样式,因此这个包在服务端渲染的时候会报错:window is not defined.

[解决办法]:兵来将挡,水来土屯。其实就是异步加载组件呗,这种组件一般来说也没必要在服务端渲染出来所以目前我的解决办法就是使用webpack的import()方法对组件进行异步加载,这样就没问题了。因此,我又额外写了一个异步加载的组件,有需要的话直接拿来用就可以了:rc-async-component。这里应该叫react-async-component的,但是奈何被人家一个月前注册了,所以说提前抢个好名字有用啊,哈哈。

使用方法非常简单,代码如下:

...
import asyncComponent from 'rc-async-component';
...
const BestInput = asyncComponent(() => import('react-best-input'));
...
render() {
    return (
        <BestInput />
    )
}

强调一下,非服务端渲染直接就可以使用,没有任何问题,服务端渲染目前需要用异步加载的方式,可能后续有更好的解决办法到时候会更新文档。

结束语

如果看到这还能坚持看下去,我真的感谢你们了,感觉自己水平有限,会不断提高的!我觉得对一个人有帮助也是有意义的事情,O(∩_∩)O哈哈~

传送门:react-best-inputrc-async-component

如果您喜欢,谢谢star,我会非常开心