input组件在微信小程序和pc端表现不一致的问题

271 阅读3分钟

对于el-input组件,正常情况下,给组件设置v-model之后,例如:

v-model="inputCouponValue"

我们就可以通过在父组件中修改inputCouponValue,然后这个更改就回传给子组件,子组件内部通过

this.$emit('input', newInputCouponValue)

来通知父组件,然后父组件会再更改inputCouponValue实现子组件的更新

但是当我们需要将父组件的值做一些处理再传入子组件后,就不能用这种写法了,例如以下写法:

// alignInputValue想要保留2位小数
v-model="alignInputValue(inputCouponValue)"

就是非法的 这时,我们需要将v-model换成value

:value="alignInputValue(inputCouponValue)"

对于这个输入框,还有一个需求,就是输入的最大值有限制,例如是10,最开始时,默认会给this.inputCouponValue赋值10(希望用户尽可能少用现金)

由于用户可能输入大于10的数字,所以很自然有个逻辑就是失去焦点时将用户输入的值再重置成10,所以组件现在是这样:

:value="alignInputValue(inputCouponValue)"
@blur="setInputCouponValue"

在setInputCouponValue中,可以通过事件对象获取输入的值,进行判断,于是有:

setInputCouponValue (e) {
	if (e.target.value > this.maxDicCouponCost) {
    	this.inputCouponValue = this.maxDicCouponCost
    }
}

此处的maxDicCouponCost就是10 期望的效果是,如果用户输入1000,失去焦点后input框中的值自动变成10 但是发现这样做没法更改input中的值,也就是说,input框中的值还是1000

经过后来的测试发现,如果手动给this.inputCouponValue赋一个常量,例如:

setInputCouponValue (e) {
	if (e.target.value > 10) {
    	this.inputCouponValue = 5
    }
}

是可以的,但如果把这里的5改为和maxDicCouponCost一样的值10,就不行了

目前观察到的现象总结: inputCouponValue初始值是10,再输入1000,失去焦点,再将inputCouponValue的值改为10,这个过程并没有触发组件内部dom的更新 inputCouponValue初始值是10,再输入1000,失去焦点,再将inputCouponValue的值改为5,这个过程触发了组件内部dom的更新 猜测: 父组件给input传的value值(在此处就是inputCouponValue)的改变会导致dom的更新(input.value = xxx),所以组件里面可能会有对value的watch监听

失去焦点时,el-input组件并没有把用户输入的值更新到对应的变量中,因为如果更新了,那inputCouponValue的值也应该随之改为1000(dom上的值改变时,一定需要通知给Vue对象去改变),我们再将1000改为10时,inputCouponValue是有变化的,也就是传给el-input组件的value是有变化的,应该是会更新dom的,但实际没更新 也就是说,我们期待input组件失去焦点时帮我们做这样一件事: 先将inputCouponValue改为1000,然后再从1000改为10,但它实际上没这么做

通过查看el-input组件的源码,发现: 在el-input组件内部,input的value绑定的是currentValue变量:

<input
  :value="currentValue"
>

因此,只有改变currentValue的值,才会触发currentValue的get钩子里收集的Dependency,也就是触发当前组件的刷新,所以我们重点关注一下currentValue什么时候被更新,后来发现源码中currentValue都是通过setCurrentValue这个方法来更新的,setCurrentValue更新的时机有两个: 1、value的变化 2、input事件触发

证明之前的猜想正确,修改的方案就是必须添加@input,通过它来触发触发inputCouponValue的修改,这样el-input组件就可以监听到value的改动,进而触发dom的更改了

changeInputCouponValue (value) {
  this.inputCouponValue = value
},

在实际实施的过程中,并非给inputCouponValue赋值来实现的,而是直接在@blur回调中拿到事件对象,进而从事件对象中拿到dom对象,然后直接通过event.target.value = xxx修改也可以实现

但是,把这个代码移植到微信小程序后,发现跑不起来,后来又发现,小程序中没法通过event.target拿到dom对象,所以不能通过这种方式直接操作dom改它的值