vue中使用v-model在表单元素上创建双向数据绑定,在官方文档中简单的提到了它的本质只是一个语法糖,在单向数据绑定的基础上,增加了监听用户输入事件并更新数据的功能;
对,它本质上只是一个语法糖,但到底是一个什么样的语法糖呢……?
简单点说,如果有这样一段模板:
<input v-model="name" type="text"/>
那么 v-model 的行为,就比较类似:
<input :value="name" @input="name = $event.target.value" type="text"/>
前段时间刚好要做一个类似百度搜索的输入框输入后实时搜索的功能,发现使用输入法 (如中文、日文、韩文等) 的时候,v-model不会在输入法组合文字过程中得到更新,打开官方文档查找后发现文档中已经提到了这种情况。官方推荐直接使用input事件来处理。可以向下面这样写:
<input :value="name" @getData="limit($event.target.value)" type="text"/>
没想到前几天再次踩坑。
<input v-model="loginForm.phoneNumber" @input="limit" type="text">
limit(e) {
e.target.value = e.target.value.replace(/[^\d]/g,'')
console.log('value值-----'+e.target.value)
console.log('model值-----'+this.loginForm.phoneNumber)
}
使用了一个函数限制输入框只能输入数字,输入其他将会被置空,在输入1时是正确的,之后我们输入一个非数字,这个非数字并被置空之后,再输入新的数字,出现了下面这样的情况:
v-model的值和value的值出现了不同,v-model无法获取到新输入的数值。
为什么会这样呢?百度半天无果,只好研究一波vue源码。如下便是vue中实现v-model绑定的方法,可以发现,在方法中有一句判断:code = `if($event.target.composing)return;${code}` ,这是什么意思呢?即当input事件是由IME (即由输入法触发)构成触发的,会直接return,不再获取值。
function genDefaultModel (
el: ASTElement,
value: string,
modifiers: ?ASTModifiers
): ?boolean {
const type = el.attrsMap.type
const { lazy, number, trim } = modifiers || {}
const needCompositionGuard = !lazy && type !== 'range'
const event = lazy
? 'change'
: type === 'range'
? RANGE_TOKEN
: 'input'
let valueExpression = '$event.target.value'
if (trim) {
valueExpression = `$event.target.value.trim()`
}
if (number) {
valueExpression = `_n(${valueExpression})`
}
let code = genAssignmentCode(value, valueExpression)
if (needCompositionGuard) {
code = `if($event.target.composing)return;${code}`
}
addProp(el, 'value', `(${value})`)
addHandler(el, event, code, null, true)
if (trim || number || type === 'number') {
addHandler(el, 'blur', '$forceUpdate()')
}
}
再看代码的其他部分,并没有发现什么还可能出现问题的地方,所以直接来测试一波是否当我们输入汉字后再输入数字时,composing的判断出现了问题,导致了v-model无法获取值。
limit(e) {
e.target.value = e.target.value.replace(/[^\d]/g,'')
console.log('是否由输入法触发-----'+e.target.composing);
console.log('value值-----'+e.target.value)
console.log('model值-----'+this.loginForm.phoneNumber)
}
果然,composing再继续输入数字时,每次判断仍然为true,导致v-model无法获取到值。