需求:
如何在项目中实现像百度搜索框一样的智能提示效果,如下图所示:
相关知识:
下面是各厂商提供的免费API
厂商 | 请求 |
---|---|
百度 | http://suggestion.baidu.com/su?wd=中国&cb=window.baidu.sug |
必应 | http://api.bing.com/qsonhs.aspx?type=cb&q=#content#&cb=window.bing.sug |
谷歌 | http://suggestqueries.google.com/complete/search?client=youtube&q=#content#&jsonp=window.google.ac.h |
好搜 | https://sug.so.360.cn/suggest?encodein=utf-8&encodeout=utf-8&format=json&word=#content#&callback=window.so.sug |
淘宝 | https://suggest.taobao.com/sug?code=utf-8&q=#content#&callback=window.taobao.sug |
以下是百度链接的结果,除了谷歌是下载一个txt文件外,其他厂商基本上都差不多,略有区别。
window.baidu.sug({q:“中国”,p:false,s:[“中国地图”,“中国移动”,“中国消防”,“中国教育考试网”,“中国知网”,“中国人事考试网”,“中国执行信息公开网个人查询”,“中国农业银行”,“中国银行”,“中国高等教育学生信息网(学信网)”]});
说明:window.baidu.sug是百度搜索的一个接口,用于获取搜索建议。通过发送请求,可以获取与输入关键词相关的搜索建议列表。其中,参数q表示搜索关键词,p表示搜索位置,bs表示输入框中的内容,csor表示是否开启拼音纠错,status表示请求状态,s表示搜索建议列表。
请求方式
-
XMLHttpRequest(XHR)
- 介绍:XMLHttpRequest是前端最早使用的数据请求方式,它支持异步请求,可以通过设置回调函数处理请求完成后的数据。
- 特点:虽然在现代浏览器中表现良好,但随着浏览器性能的提升,其性能瓶颈逐渐凸显。此外,XMLHttpRequest的语法相对复杂,使用起来不够直观。
- 使用场景:适用于一些老旧项目或者需要与多种后端系统交互的场景。
-
Fetch API
- 介绍:Fetch是一个现代的、基于Promise的HTTP客户端,用于浏览器和Node.js。它提供了一种更简洁、更易于理解的方式来处理网络请求。
- 特点:Fetch API的语法简洁、易读,且返回Promises,使得异步操作更加易于管理和链式调用。此外,Fetch API还支持跨域请求和流式响应。
- 使用场景:适用于现代浏览器中的网络请求处理。
-
Axios
- 介绍:Axios是一个基于Promise的HTTP客户端,用于浏览器和Node.js。它扩展了Fetch API,提供了更丰富的功能。
- 特点:Axios支持请求和响应拦截器、自动转换JSON数据、取消请求等功能。此外,它还提供了统一的错误处理机制。
- 使用场景:适用于需要处理复杂网络请求的场景,如添加请求和响应拦截器、自动处理JSON数据等。
-
表单提交
- 介绍:通过HTML表单提交数据到服务器。
- 特点:表单提交是同步请求,会刷新页面。但可以通过设置表单的
method
属性为POST
来避免在URL中暴露数据。 - 使用场景:适用于简单的数据提交场景。
-
JSONP
- 介绍:一种通过
<script>
标签的src
属性跨域请求数据的方式。 - 特点:JSONP只能发送GET请求,且存在安全风险(如XSS攻击)。
- 使用场景:适用于解决跨域问题,但现代浏览器更推荐使用CORS(跨源资源共享)来解决跨域问题。
- 介绍:一种通过
实现:
第一步:Element 提供了 el-autocomplete 组件 el-autocomplete
第二步:发送请求,使用的是 axios ,通过 fetch 组件发送,发现请求成功了,但是无法接受返回的数据
问题:因为API返回的是JSONP数据,JSONP是跨域访问的一种方式,网上很多都是用的原生的 js 代码去发送请求,嵌入到vue中不太方便,因此我想着vue能不能直接发送jsonp 请求,后面发现vue-resource可以发送,但是Vue3项目当前无法继续使用vue-resource进行http请求。
什么是JSONP接口:浏览器端通过
- JSONP 不属于真正的 Ajax 请求,因为它没有使用 XMLHttpRequest 这个对象
- JSONP 仅支持 GET 请求,不支持POST、PUT、DELETE 等请求
如果项目中已经配置了 CORS跨域资源共享,为了防止冲突,必须在配置 CORS 中间件之前声明 JSONP 的接口。否则 JSONP 接口会被处理成开启了 CORS 的接口,对于 JSONP,你应该使用 XMLHttpRequest 或者动态创建一个
解决:暂时直接通过后端去调用,响应速度还可以,后面前端调用好了,再来更新此文。
// 核心代码如下,包括发送请求和解析百度接口的数据
String re = HttpRequest.get(url).execute().onSuccess(ResponseSpec::asString);String regex = "s:\\[(\"(.*?)\"(?:,\\\"(.*?)\\\")*)\\]";Pattern pattern = Pattern.compile(regex);Matcher matcher = pattern.matcher(re);if (matcher.find()) {// 提取匹配到的内容,但这里需要额外的处理,因为正则表达式无法直接处理嵌套的引号// 我们可以尝试通过分割逗号来获取每个元素,但需要注意处理可能的转义字符(在这个特定例子中,我们假设没有转义字符)String arrayString = matcher.group(1);// 移除首尾的多余字符(如果有的话)arrayString = arrayString.trim().replaceAll("^\"|\"$", ""); // 移除首尾的双引号(如果有的话)// 分割字符串获取每个元素String[] elements = arrayString.split("\",\""); // 分割逗号,但注意这里假设没有逗号在引号内// 将元素装入列表List<String> sList = new ArrayList<>();for (String element : elements) {// 可能需要额外的处理来移除任何额外的引号或空格sList.add(element.trim().replaceAll("^\"|\"$", "")); // 移除首尾的双引号(如果有的话)}// 将sList封装为List<Object>返回即可,Object中包含value属性......} else {log.error("未找到匹配的 s 数组内容。");}
前端通过XMLHttpRequest,仅供参考
function querySearch(queryString, cb) {// 创建一个唯一的回调函数名,以避免命名冲突const callbackName = `baiduSugCallback_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;// 在全局作用域中定义回调函数window[callbackName] = function(data) {// 调用传入的回调函数,并传入解析后的数据cb(data);// 清理全局回调函数delete window[callbackName];};// 创建并配置一个 JSONP 请求const xhr = new XMLHttpRequest();const url = `https://suggestion.baidu.com/su?wd=${encodeURIComponent(queryString)}&cb=${callbackName}`;xhr.open('GET', url, true);xhr.send();// 处理请求失败的情况(可选)xhr.onerror = function() {// 可以在这里处理错误,例如调用 cb 函数并传入一个错误对象delete window[callbackName]; // 确保在出错时也清理回调函数cb(new Error('JSONP request failed'));};
}// 使用 querySearch 函数
querySearch('美国', function(data) {console.log(data); // 输出解析后的数据
});