前言
无论是pc端还是移动端,在业务中,表单值的校验和搜索功能都是非常常见的需求。下面总结自己在使用vue、react实现这些功能时踩过的坑。
表单校验、搜索功能的实现
表单校验
表单校验需求中常见的例如:匹配某种规则(企业税号格式的校验、电话号码(手机号)的校验、身份证的校验...)、对内容长度限制、对输入字符类型的限制等等。
Vue中的实现:用input事件来对输入框的值进行处理
Vue中需要特别注意的是在处理中文时对事件input和keyup之间的选择,选择input事件。
坑点:
input事件和keyup事件在输入时触发事件的表现上没有区别,input是值改变就触发,keyup是键盘释放时就触发。当在输入中文未选内容鼠标离开时,不会触发keyup事件,但是会触发input事件。如下例子,限制输入框字符数不超过32个:
限制字符数代码(中文算2个,英文算1个):
limitLength(e) {
const input = e.target
const value = input.value
const split = value.split('')
const map = split.map((s, i) => {
return value.charCodeAt(i) >= 0 && value.charCodeAt(i) <= 128
? 1
: 2
})
let n = 0
const charLength =
map.length > 0 &&
map.reduce((accumulator, currentValue, index) => {
const count = accumulator + currentValue
if (count === 31 || count === 32) {
n = index
}
if (count > 32) {
this.name = split.slice(0, n + 1).join('')
// this.$emit("setUserName",split.slice(0, n+1).join(''));
}
return count
})
},
keyup事件效果
鼠标离开时没有触发回调函数(输入中文拼音时字符数超过了32个还能显示)。
input事件效果
鼠标离开,触发回调函数。
Vue中实现带校验功能的FormInput输入框组件:
<template>
<input
@input="methodKeyUp"
v-model="currentValue"
>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String,
},
data() {
return {
currentValue: '2222',
};
},
methods: {
limitLength(value, maxWordsCount) {
return value && value.length > maxWordsCount
? value.slice(0, +maxWordsCount)
: value;
},
async methodKeyUp(v) {
const target = v.currentTarget || v.target;
let arr = [];
let val = target.value;
if (val) {
val = val.toString().trim();
val = this.limitLength(val, 6);
arr = val.match(/^[\u4e00-\u9fa5]+(\d+)?$/);
if (
arr &&
Object.prototype.toString.call(arr) == '[object Array]' &&
arr.length
) {
val = arr[0];
} else {
val = '';
}
this.currentValue = val;
}
},
},
};
</script>
额外说明: 在使用vue实现回车换行这样的功能时使用的keyup事件有更好的体验
keyup: function(e) {
//Ctrl + Enter发送消息
if (e.keyCode == 13 && e.ctrlKey) {
this.content += `\n`
let el = this.$el.querySelector('.input-textarea')
el.scrollTop = el.scrollHeight - el.clientHeight
}
},
校验与搜索并存
React中的实现:需封装原生的compositionstart/compositionend事件
React中input表单没有input事件,那么如何来实现像vue中input事件那样处理中文输入的效果呢?
FormInput业务组件:
limitLength(value, maxWordsCount) {
return value && value.length > maxWordsCount
? value.slice(0, +maxWordsCount)
: value;
},
//值改变先校验值再去查询
filterInputValue = async (v: any) => {
const target = v.currentTarget || v.target
let arr = []
let val = target.value
//this.setState({ isShowOptions: true })
await this.props.onHandleChange(val) //更新父组件value值
if (val) {
val = val.toString().trim()
val = this.limitLength(val, 6)
arr = val.match(/^[\u4e00-\u9fa5]+(\d+)?$/)
}
if (arr && Object.prototype.toString.call(arr) == '[object Array]' && arr.length) {
val = arr[0]
} else {
val = ''
}
await this.props.onHandleChange(val) //更新父组件value值
this.searchValue(val) //再查询
}
React版自定义输入表单inputField组件,封装compositionstart/compositionend:
import * as React from 'react'
import PropTypes from 'prop-types'
import './input-field.less'
// detect whether it is chrome
// const isChrome = !!window.chrome && !!window.chrome.webstore
const noop = () => {}
interface InputField {
isOnComposition: Boolean,
emittedInput: Boolean,
}
class InputField extends React.PureComponent<any, any> {
static propTypes = {
value: PropTypes.any,
defaultValue: PropTypes.any,
onChange: PropTypes.func,
onInputChange: PropTypes.func
}
static defaultProps = {
onChange: noop,
onInputChange: noop
}
constructor(props:any, context:any) {
super(props, context)
this.state = {
value: this.props.value || this.props.defaultValue || ''
}
this.isOnComposition = false
this.emittedInput = true
}
componentWillReceiveProps(nextProps:any) {
const { value } = nextProps
if (value !== this.state.value) {
this.setState({
value
})
}
}
handleInputChange = (event:any) => {
let userInputValue = event.target.value
this.setState({
value: userInputValue
})
if (!this.isOnComposition) {
event.target.value = userInputValue
this.props.onInputChange(event)
this.emittedInput = true
} else {
this.emittedInput = false
}
this.props.onChange(userInputValue)
}
handleComposition = (event:any) => {
if (event.type === 'compositionstart') {
this.isOnComposition = true
this.emittedInput = false
} else if (event.type === 'compositionend') {
this.isOnComposition = false
// fixed for Chrome v53+ and detect all Chrome
// https://chromium.googlesource.com/chromium/src/+/afce9d93e76f2ff81baaa088a4ea25f67d1a76b3%5E%21/
// also fixed for the native Apple keyboard which emit input event before composition event
// subscribe this issue: https://github.com/facebook/react/issues/8683
if (!this.emittedInput) {
this.handleInputChange(event)
}
}
}
handleKeyUp=(e:any)=> {
const { onPressEnter, ...restProps} = this.props
var event = e || window.event;
var key = event.which || event.keyCode || event.charCode;
if (key == 13) {
onPressEnter(event)
/*Do something. 调用一些方法*/
}
}
render() {
const { onInputChange, onBlur, onPressEnter, ...restProps} = this.props
return (
<div>
<input
type='text'
{...restProps}
value={this.state.value}
onChange={this.handleInputChange}
onCompositionStart={this.handleComposition}
onCompositionEnd={this.handleComposition}
onBlur={onBlur}
onKeyUp={this.handleKeyUp}
/>
</div>
)
}
}
export default InputField
总结
React和Vue中的input控件在处理中文时有些不同,Vue中通过@input事件可以直接在输入框输入中文并选中中文时再触发事件,但是React没有@input事件,需要对原生input表单的compositionStart和compositionEnd两个事件额外进行控制达到类似效果。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。