Vue组件更新,同时在输入中键入清除文本



我认为这是组件更新时的反应性问题。基本上,如果我在输入中快速键入,并且父数据得到更新,文本有时可能会丢失。

你最好在下面试试,只需快速键入,你就会看到字母消失。

const search = Vue.component('search', {
props: [ "suggestions", "settings", "search" ],
template: `
<div>
<input type="text" placeholder="Search..." :value="search" @keyup="$emit('update:search', $event.target.value)">
<p v-for="suggestion in suggestions">{{ suggestion }}</p>
</div>
`
})
new Vue({
data: {
settings: {},
state: {
autocompletedSuggestions: []
},
search: ""
},
watch: {
search(value) {
const dummyData = [ 'test1', 'test2', 'test3', 'test4', 'test5', 'test6', 'test7', 'test8' ]
window.setTimeout(() => {
let random = Math.round(Math.random() * (dummyData.length - 0) + 0);
let suggestions = [];
for (let i = 0; i < random; i++) {
suggestions.push(dummyData[Math.round(Math.random() * (dummyData.length - 1) + 0)])
}
this.state.autocompletedSuggestions = suggestions
}, 100)
}
}
}).$mount('#app')
p {
padding: 0;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<search :suggestions="state.autocompletedSuggestions"
:settings="settings"
:search.sync="search"></search>
</div>

我该如何阻止这种情况的发生?

这只是由引起的时间问题

  1. keyup事件有一个显著的延迟(在我的机器上在50到80ms之间——我想这也取决于键盘)
  2. 非常";方便地";选择";超时";在您的示例中,setTimeout的值(如果您尝试的时间明显更短,如10ms,或更长,如1000ms,则出现所述不当行为的几率将降低)

发生了什么:

  1. 第一个";a";键入的信件->事件
  2. keyup事件
  3. CCD_ 4更新为"0";a"+观察程序设置超时
  4. 组件已更新
  5. 第二个";a";打字的信
  6. 更新this.state.autocompletedSuggestions执行超时功能
  7. 组件已更新-";a";作为CCD_ 6道具被推送-有效地将输入值从"0"改变为"0";aa";至";a">
  8. keyup事件(对于"aa")-但是$event.target.value已经被设置为"aa";a">

如何修复:

  1. 不要使用keyup事件。请改用input。它的启动速度要快得多(在用户输入后)。当用户使用上下文菜单粘贴并为每个键(包括Shift、Alt等)激发时,keyup不会被激发,这不是您想要的
  2. 使用某种反跳-只有在用户停止键入后才触发搜索请求。400毫秒对我来说很好。在您的示例中用于模拟ajax调用的setTimeout实际上很好(请参阅我的示例)
  3. 每当您触发新的搜索请求时,请确保取消前一个请求(如果有)。或者只是在请求过程中禁用输入

const search = Vue.component('search', {
props: ["suggestions", "settings", "search"],
template: `
<div>
<input type="text" placeholder="Search..." :value="search" @keyup="onKeyup" @input="onInput">
<p v-for="suggestion in suggestions">{{ suggestion }}</p>
</div>
`,
methods: {
onKeyup(ev) {
console.log(`Keyup event - Value: ${ev.target.value}`)
},
onInput(ev) {
console.log(`Input event - Value: ${ev.target.value}`)
this.$emit('update:search', ev.target.value)
}
},
updated() {
console.log("Updated")
}
})
new Vue({
data: {
settings: {},
state: {
autocompletedSuggestions: []
},
search: "",
timeout: 0,
debounceTime: 400
},
watch: {
search(value) {
console.log("Search:", value)
clearTimeout(this.timeout) // cancel previous timeout if any
this.timeout = window.setTimeout(async () => {
this.state.autocompletedSuggestions = await this.getSuggestionsAsync()
console.log("Suggestions loaded")
}, this.debounceTime)
}
},
methods: {
getSuggestionsAsync() {
return new Promise((resolve) => {
const dummyData = ['test1', 'test2', 'test3', 'test4', 'test5', 'test6', 'test7', 'test8']
let random = Math.round(Math.random() * (dummyData.length - 0) + 0);
let suggestions = [];
for (let i = 0; i < random; i++) {
suggestions.push(dummyData[Math.round(Math.random() * (dummyData.length - 1) + 0)])
}
// to simulate async call
setTimeout(() => resolve(suggestions), 100)
})
}
}
}).$mount('#app')
p {
padding: 0;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<pre>{{ search }}</pre>
<search :suggestions="state.autocompletedSuggestions" :settings="settings" :search.sync="search"></search>
</div>

最新更新