一、问题
做搜索功能时,监听input的value值变化实时请求数据,每增加一个字符,就发送一次请求。例如输入12345,会监听到5次变化1 12 123 1234 12345,导致连续请求5次。这是没必要的,增加服务器负担,可能出现卡顿的情况。
甚至,一定概率下会发生请求返回顺序问题,当输入速度很快时,连续发请求,服务器可能先返回12345请求的结果,然后再返回12请求的结果,导致最后页面上显示的结果是12请求到的结果列表,而不是想要搜索的12345的结果。一定程度上降低这种情况。
二、想法
不希望input框中值一改变,就马上去请求数据,而是在指定间隔内没有输入时,才会执行函数。如果停止输入但是在指定间隔内又输入,会重新触发计时。这就是函数防抖。
实时监听input值的变化,输入一个字符,300ms后再去获取input中的输入值。
三、关键代码
<input ref="searchInput" v-model="inputVal" type="text" placeholder="搜索目的地" maxlength="20"/>
data() {
return {
inputVal: '',
timeout: null,
}
}
watch: {
inputVal(curVal, oldVal) {
// 实现input连续输入,只发一次请求
clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
this.getListPOI(curVal);
}, 300);
}
}
四、再优化,解决异步请求可能发生先请求后返回问题,导致结果列表不是预料展示的效果
假如300ms的延迟,服务器还是可能存在先发送的请求后返回。避免先请求后返回问题,确保以当前输入的值12345为参数给结果表赋值,可以在请求里做判断。当请求返回结果时,判断请求的参数inputVal和当前输入框的值this.inputVal相等,才进行相关操作:if (this.inputVal === inputVal) {...}
methods:{
/**
* poi模糊搜索列表
*/
async getListPOI(inputVal) {
if (inputVal === '') {
return false;
}
this.searchPoi = [];
this.divLoading = true;
if (!navigator.onLine) { // 没有网络
this.onLine = false;
this.divLoading = false;
return false;
} else {
this.onLine = true;
}
try {
const res = await getListPOI({
"buildId": this.$store.state.showBuildId,
"coor": 1,
"floorId": this.$store.state.showFloorId,
"latitude": this.$store.state.userCurLoc[1] || 0,
"longitude": this.$store.state.userCurLoc[0] || 0,
"name": inputVal,
"pageNum": 1,
"pageSize": 30
});
if (this.inputVal === inputVal) { // 关键代码 避免先请求后返回问题,确保给列表赋值是以当前输入的值为参数的
if (res.code === 0 && res.data) {
let data = res.data;
if (data.length === 0) {
this.hasResult = false;// 没有结果
this.searchPoi = [];
} else { // 有结果
this.hasResult = true;
this.searchPoi = data;
}
} else {
this.searchPoi = [];
console.log("请检查/poi/list/POI接口返回的数据格式");
}
this.divLoading = false;
}
} catch (err) {
this.divLoading = false;
console.log("poi模糊搜索列表接口请求失败", err);
}
}
}