vue自动完成搜索功能的数据请求处理
在现在的互联网世界里,自动完成的搜索功能是一个很常见的功能。比如百度、搜狗、360搜索 ...
功能描述一下大概是这个样子的:有一个搜索框,用户在里面输入要查询的条件,系统会“智能”判断用户输完了,然后自动根据条件去搜索相关的数据返回给用户。
网上这个自动完成的插件很多,实现自动完成功能也不复杂,特别是像vue、angularjs、react这类可以实现双向绑定的库出现以后,实现就更方便了。本文不讲自动完成功能的实现,而是介绍自动完成功能后续数据的请求该如何考虑,主要要处理下面两个问题。
问题1:自动完成搜索触发频率如何控制?
问什么要控制自动完成搜索的触发频率呢?大家都知道,自动完成触发基本上都是键盘响应事件,在文本框中输入一个文本就触发一次,这个太频繁了。比如我要输入的搜索条件是abcdefg,输入a触发一次,接着输入b再触发一次,接着输入c又触发一次...,等到我把这几个字母全部输完就触发了7次,假设请求逻辑没有限制的话,这就会发生7次数据请求,而实际上只有第7次的结果才是我想要的,其它6次数据请求完全是浪费资源。在用户需求上来说,用户真正希望的是搜索框能自动识别到用户的abcdefg这一串字符全部输入完了,然后再触发请求数据。对于一些变态的输入,比如按住某一个建不放,还不知道要触发多少次。
所以控制自动完成搜索的触发频率还是很有必要的。当然,搜索框能完全智能的知道用户所想然后去触发,在目前来说还是做不到的。
我这里使用控制空闲时间的间隔的方式来限制自动完成搜索的触发频率。使用lodash库中的debounce方法。
问题2:数据还在请求中时再次触发请求,上次的请求如何取消?
为什么要取消上次的请求你?举个例了,我输入了查询条件"xxx",数据请求发送了,我们暂且把它称为请求1。因为某些原因(比如说网络不好,这个条件的数据量大等等),请求1响应很慢,我们在请求1还没用拿到数据的时候又输入查询条件"yyy"发送了请求2,没想到这个请求2数据响应特别快,一下子就得到了数据data,我们准备把data展示出来,这时候请求1的数据回来了,data会被覆盖掉,也就是说这时候我们用"yyy"的条件查询得到了"xxx"条件的查询结果。这个结果其实是超出用户期望的。所以在发送新的请求之前,如果上次的请求还没有结束,我们就需要取消掉它。
我这里使用axios的CancelToken来取消请求。
下面列出主要代码:
hello.vue:
<template> <div class="hello"> <!--使用mint-ui的搜索组件--> <mt-search v-model="searchKey" @input="search"></mt-search> <span>{{result}}</span> </div> </template> <script> import _ from 'lodash'; //引入lodash import axios from 'axios' //引入axios //请求canceltoken列表 let sources = []; export default { name: 'hello', data () { return { searchKey: '', //查询条件 result: '' //查询结果 } }, methods: { //使用_.debounce控制搜索的触发频率 //准备搜索 search: _.debounce( function () { let that = this; //删除已经结束的请求 _.remove(sources, function (n) { return n.source === null; }); //取消还未结束的请求 sources.forEach(function (item) { if (item !== null && item.source !== null && item.status === 1) { item.status = 0; item.source.cancel('取消上一个') } }); //创建新的请求cancelToken,并设置状态请求中 var sc = { source: axios.CancelToken.source(), status: 1 //状态1:请求中,0:取消中 }; //这个对象加入数组中 sources.push(sc);
//开始搜索数据,yourhttp替换成你自己的请求路径 axios.get('yourhttp', { cancelToken: sc.source.token }).then(function (res) { //请求成功 sc.source = null; //置空请求canceltoken //TODO这里处理搜索结果 console.log(res.data); that.result = res.data; }).catch(function (thrown) { //请求失败 sc.source = null; //置空请求canceltoken //下面的逻辑其实测试用 if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { //handle error } }); }, 500 //空闲时间间隔设置500ms ) } } </script> <style scoped> .mint-search { height: auto; } </style>
我的测试效果图: