在使用
Vant
组件van-field
时需要限制数值类型的最大值,尝试了两种方法,最终确定了一种最优方案
版本说明
"vue": "^2.6.8"
"vant": "^2.9.0"
因为在组件van-field
外面有嵌套了一层作为自定义组件,方便写一些公共的业务逻辑,就是因为嵌套了一层才导致我最初使用的方法有性能问题和不能准确限制最大值的问题
而且使用的是数字,所以设置type='digit'
Tips: digit 类型从 2.4.2 版本开始支持
我们先来看第一种方法
事件传递法
取这个名字的原因是因为所有都是通过事件来完成的,并且是连续的事件。
定义自定义组件
c-input
使用自定义组件
首先输入1,可以正常显示1;我们再次输入1,这时显示11;我们继续输入1,这时显示50,但是已经能看到我们由111变成50的过程。
我们继续输入1,这时显示501,而且不会变成50,查看vue-devtools
中events
的事件都发送成功,
只有在输入框失去焦点的时候才能变成50
分析
想了一下可能是因为自定义组件c-input
中的value
值始终没有改变,导致van-field
中的值不会改变
尝试一
既然没有改变,是否可以使用setTimeout
,在开始改变的时候先改变值为其它值,然后再改变成我们期望的值。
inputHandler (value) {
value = this.max > 0 ? Math.min(value, this.max || 0) : value
this.$emit('input', Date.now())
setTimeout(() => {
this.$emit('input', value)
}, 0)
},
这种做法会导致事件的死循环,发送第一次input
事件会触发vant-field
的input
事件,然后继续发送第一次input
事件,导致死循环
这时如果修改第一次input
事件的值为空字符串,也还是没达到目的。
这里我很想说先发送第一次
input
事件,再发送第二次input
事件,然后就开始死循环,也许是我没有深入研究,结果就是每次触发vant-field
组件input
事件的值都是第一次input
事件的值。有知道了麻烦在评论区回复一下
尝试二
尝试在computed中增加一个model,作为vant-field
的v-model
,同时去掉组件上:value
数据和input
事件监听
model: {
get () {
return this.value
},
set (val) {
this.inputHandler(val)
},
},
其实效果和尝试一是一样的
思来想去只能使用最后一个方法,解耦事件(当然代码要多写一点点,也就是一点点而已)
解耦事件法
在c-input
组件内定一个data
数据叫testValue
(这个变量在自定义组件外部是看不到的),同时vant-field
增加v-model='testValue'
并去掉:value
属性
还是要继续监听vant-field
的input事件,在事件处理函数中将处理好的数据赋值给testValue
(这时vant-field
的值的变化是肉眼看不到的),还是要继续发送input事件,使c-input
组件使用者知道自定义组件内部值是多少。
但是还有个问题,如果组件外部值变化,c-input
组件内部不知道值的变化,这时就需要监听value
了然后赋值给testValue
watch: {
value (newVal) {
this.testValue = newVal
},
},
同时如果创建创建的时候有初始值也需要赋值操作
created () {
this.testValue = this.value
},
运行效果杠杠的,而且当连续按住键盘不放的时候不会出现卡顿的现象。
这个处理逻辑最主要的是把
:value="value"
变成了v-model="textValue"
,把组件值的改变由事件传递解耦,变成了各个组件各自内部的逻辑,减少了相互的依赖。
其实还是那四个字,单一原则
完整代码
看到这里有人可能会问最后发送了input
事件,必然会触发value
的watch
,导致给textValue
赋值,最后还是会触发vant-field
的input
事件,导致死循环。
其实是不会的,触发value
的watch
后赋值的value
和textValue
是相同的,不会触发vant-field
的input
事件导致死循环。