mpvue
mpvue copied to clipboard
input光标闪烁
在input基础上自定义组件,实现v-model,输入框光标闪烁

感觉不是单纯的 v-model 问题,可否提供下 demo
同样的问题,因为v-mode是通过bindinput事件实现的
@rchunping 之前觉得也是这个原因,但是我在onSearchChange里不赋值就不会闪烁
@youngluo 按照bindinput的说明,事件处理函数中如果返回了字符串,那么会替换input中的内容,我猜问题出在这里?
@rchunping 请问这个问题解决了吗?
@xiaoyang3874 不用v-mode , 自己 @input , 在input事件里更新
<input :value="value" @input="onInput" />
data() {
return {
value:''
}
}
methods: {
onInput (e) {
this.value = e.mp.detail.value
}
}
@rchunping 这样的话,当你进行输入数据修改的时候,比如输入了一串数字,修改中间几个,当点击第一次删除的时候,光标会自动移动到最后,同理,在插入的时候,刚输入一个字的时候,光标也会移动到最后,变成了除了第一个,其他的都在尾部删除或追加了
@sharkdong 是的,安卓正常,iOS上会有你说的情况
@rchunping 那有什么好的解决方案吗?这边比较头疼,看到美团官方提供的 美团汽车票的小程序,在搜索框输入的时候,也出现这个问题,用户体验特别不好,现在用户要我们改,唉😔
@sharkdog 那不用实时获取输入内容啊,@confirm 或者表单@submit 都可以拿到值
same issue
使用 lazy 修饰符绑定应该不会有这个问题吧 v-model.lazy="text"
@F-loat 如果有v-model.lazy的话,会出现另外一个问题,因为lazy修饰符是触发change事件,而mpvue会将change事件变成blur事件,所以就会导致,如果填写好表单,并没有移开光标,直接点击提交,该字段会没有提交到后台,因为blur事件还没有触发,所以数据还没有绑定
@sharkdong 我这边开发者工具中,输入后直接点击 button 提交,获取到的值是没问题的,真机暂时还没测试
@sharkdong 我这边是利用input的回调来更新绑定的变量的值的,input上不绑定value,当表单提交后设置一个isClear状态置为true,使用假的input项展示默认的提示。
伪代码如下:
<view>
<view
@click="handleResetClearInput"
v-if="!cmtInput.focus && cmtInput.isClear"
class="fake-input">{{ cmtInputPlaceholder }}</view>
<input
v-if="!cmtInput.isClear"
:focus="cmtInput.focus"
:placeholder="cmtInputPlaceholder"
type="text"
@input="onCmtInput"
@confirm="onCmtInputSubmit"
@click="cmtInput.focus = true"
/>
<i class="icon-send" :class="{ 'is-disabled': cmtInput.content.length === 0 }" @click="onCmtInputSubmit"></i>
</view>
data () {
return {
cmtInput: {
isClear: true,
content: '',
focus: false
}
}
},
methods: {
handleResetClearInput () {
this.cmtInput.focus = true
this.cmtInput.isClear = false
this.cmtInput.content = ''
},
onCmtInput (e) {
this.cmtInput.content = e.mp.detail.value
},
onCmtInputSubmit () {
this.cmtInput.focus = false
// 发布
publish().then(() => {
this.cmtInput.focus = false
this.cmtInput.isClear = true
this.cmtInput.content = ''
})
}
}
@F-loat 安卓机没问题的,但是那个苹果机子会出现这样的问题,我测试过,包括美团自己的小程序,美团汽车票都会出现这样的问题
@isunkui 你这样的话,也可以,但是如果初始值需要的话,你怎么填充,比如刚进来,输入框的初始值是从后台拿到的,需要双向绑定,还是躲不掉用value或者v-model,还是会出现这样的问题
@sharkdong 如果需要用到双向绑定确实没找到很好的解决方案,目前我们的产品设计上不存在这个问题。
@isunkui 这样的方法也考虑过,可能页面用到的内容比较多,所以就想了想放弃了,不过仔细想一下,只要存在双向绑定,应该都会出现这个问题,在input和textarea上。后来针对这个单页面,使用原生吧,可能这样好点
是的 望优化一下吧
因为在播放音频 使用了vuex一秒会commit一次拿去实时时间
然后input会出现莫名其妙的问题
一直在输入中。。。
@youngluo @F-loat 遇到这个问题, 调了一个晚上, 弄了个workaround:
const vModelBugfillMixin = {
data() {
return {
ownValue: null,
};
},
model: {
prop: 'valueFromProp',
},
props: {
valueFromProp: {
type: String,
},
},
computed: {
value() {
return this.ownValue == null ? this.valueFromProp : this.ownValue;
},
},
methods: {
onChange(event) {
this.ownValue = event.target.value;
setTimeout(() => this.$emit('input', event.target.value));
},
},
};
export default vModelBugfillMixin;
首先思路来自这个comment, 根据这个一路在 mpvue 源码里面 找到了 updateDataToMP方法,
function updateDataToMP () {
var page = getPage(this);
if (!page) {
return
}
var data = formatVmData(this);
console.log('updateDataToMP', data);
throttleSetData(page.setData.bind(page), data);
}
打了log之后发现, 每次更新的时候, 输出结果如下:

点开看了一下, 每次对一个组件进行更新的时候 其实包括其children都进行了更新, 所以闪烁的原因很简单了:
考虑一个3层的双向数据流: page.value <=> component.props.value <=> input.value
每当用户输入时, 按顺序发生以下时间:
- input.value 先变了 (界面上有反应, 比如
1变成了12) - input 元素触发了 'input' 事件
- component 拦截并继续传播 'input' 事件 的
e.target.value - page.value 发生了改变, 事件结束 中间再发生些什么...
- page 用小程序的 setData 接口更新数据, 也就是日志的第一行
$root.0(界面上12变回了1) - component 用小程序的 setData 接口更新数据, 也就是日志的第一行
$root.0,0(界面上1又变成了12) - input 用小程序的 setData 接口更新数据
闪烁的关键就出现在第5步, 因为这个时候component.props.value 还是旧的值, 但page对vmData的更新包括了其children的vmData
解决方案就一目了然了:
- 让第 1 步不变, 我们除非我们用一个假的input罩在用户实际输入的input上面, 让假的input最后更新
- 让第 5 步不变, 修改mpvue源码, 不要更新children, 但显然牵扯更多,
- 让第 5 步和第 6 步调换顺序, 先把component.value更新了, 就不怕page的更新了
最终使用的是方案3, 这样的代价是2次更新变成了3次更新, 如果一共有n层的话 就是 n 变成 (2n - 1)

我在component中,使用computed赋到input的v-model上, get为先取组件内部raw,取不到则使用value set时,会触发一个方法提交数据到vuex。
editing: {
get() {
return this.raw || String(this.value)
},
set(val) {
this.raw = val
this.setFieldVal({
fieldName: this.fieldKey,
value: val
})
}
},
参考 @Stupidism 的分析,添加nextTick,确保先触发子组件的,然后触发父组件的setData 效果是,闪烁减少了。但依然有一点儿。使用中文输入法的时候,输入太快就有影响。
editing: {
get() {
return this.raw || String(this.value)
},
set(val) {
this.raw = val
this.$nextTick(()=>{
// 延迟提交vuex, 确保先更新children,减少mpvue闪烁
this.setFieldVal({
fieldName: this.fieldKey,
value: val
})
})
}
},
使用了lazy修饰符导致不能实时更新了
不用组件,问题解决!
不用组件 还真有点用。。。。
抱歉我不会复制代码....
@sharkdong @isunkui @youngluo 一个除了不支持赋值给input,其它问题都解决了的hack方案。
- 不给input绑定value,使用其它变量记录input的值;
- 使用v-if指令重置节点,模拟清空input。

<template lang="html">
<div class="search-input">
<img :src="searchIcon" class="search-icon" />
<!-- 因mpvue的bug(http://mci.sankuai.com/osx/travel-with-idol/issues/19),帮能绑定value -->
<input
v-if="showInput"
type="text"
@input="handleInput"
confirm-type="search"
class="input"
:placeholder="placeholder"
placeholder-style="color: #999; font-size: 30rpx"
>
<img
v-if="_value"
:src="clearIcon"
@click="handleClear"
class="clear-icon"
/>
</div>
</template>
<script>
import searchIcon from '@/common/assets/search.svg'
import clearIcon from '@/common/assets/clear.svg'
export default {
name: 'search-input',
props: {
placeholder: {
type: String,
default: '',
},
},
data() {
return {
_value: '',
searchIcon,
clearIcon,
showInput: true,
}
},
methods: {
handleInput(evt) {
this._value = evt.target.value
this.$emit('input', evt.target.value)
},
handleClear() {
this.$emit('input', '')
this._value = ''
// 未绑定input value的下,模拟清空value
this.showInput = false
setTimeout(() => {
this.showInput = true
}, 10)
}
},
}
</script>
mark
