Vue.js keyup触发WP API搜索时请求滞后结果错乱问题
问题根因
你遇到的是典型的异步请求竞态问题:所有触发的搜索请求没有做有效性校验,不管发起时间先后,只要返回就会直接覆盖页面数据,网络延迟波动时,更早发出的慢请求很容易晚于新请求返回,直接把新请求的正确结果覆盖掉。你之前加的固定延迟只是把所有请求的执行时间整体后移,完全没有处理“旧请求结果无效”这个核心问题,自然解决不了bug。
另外你代码里还有几个隐性问题:
- URL拼接错误使用了HTML转义字符
&,实际请求会把amp;当成参数名的一部分,导致接口参数解析异常 - 搜索关键词没有做URL编码,输入特殊字符时会直接导致请求失败
- 输入框清空时没有触发默认文章加载逻辑,也是清空后结果异常的诱因之一
修复思路
核心是保证只有最新发起的请求结果能更新页面,配合减少无效请求:
- 新增请求版本戳:每次发起新请求时版本号自增,请求返回后先校验版本号和当前最新版本是否一致,不一致直接丢弃结果
- 新增请求中断逻辑:用原生
AbortController,每次发起新请求时直接中断还在等待响应的旧请求,从根源减少无效响应 - 输入事件加防抖:用户停止输入300ms后再发请求,避免每按一次键就发一次无意义请求
- 补全空搜索词判断:输入框清空时直接加载默认的首页文章列表
修复后核心代码
const websiteurl = 'https://www.aaps.ca'; var vm = new Vue({ el: '#blog-page', data: { noData: false, blogs: [], page: 0, search: '', totalPagesFetch: "", apiURL: `${websiteurl}/wp-json/wp/v2/posts?per_page=6`, searchbyid: `${websiteurl}/wp-json/wp/v2/posts?per_page=6&include=`, searchUrl: `${websiteurl}/wp-json/wp/v2/search?subtype=post&per_page=6&search=`, // 新增请求控制字段 requestVersion: 0, currentAbortController: null, searchTimer: null }, created: function () { this.fetchblogs(); }, methods: { fetchblogs: function () { this.page = 1; // 中断未完成的旧请求 if (this.currentAbortController) this.currentAbortController.abort(); const controller = new AbortController(); this.currentAbortController = controller; // 标记当前请求版本 const currentVersion = ++this.requestVersion; fetch(this.apiURL, { signal: controller.signal }) .then(response => response.json()) .then(data => { // 仅最新请求结果允许更新页面 if (currentVersion === this.requestVersion) { this.blogs = data; this.noData = data.length === 0; } }) .catch(err => { // 主动中断的请求无需额外处理 if (err.name !== 'AbortError') console.error(err); }); }, searchonurl: function () { // 输入防抖 if (this.searchTimer) clearTimeout(this.searchTimer); this.searchTimer = setTimeout(() => { const keyword = this.search.trim(); // 空关键词直接加载默认文章 if (!keyword) { this.fetchblogs(); return; } this.searchPagination(1); }, 300); }, searchPagination: function (page) { // 中断未完成的旧请求 if (this.currentAbortController) this.currentAbortController.abort(); const controller = new AbortController(); this.currentAbortController = controller; const currentVersion = ++this.requestVersion; this.page = page; const searchword = this.search.toLowerCase().trim(); const url = `${this.searchUrl}${encodeURIComponent(searchword)}&page=${this.page}`; fetch(url, { signal: controller.signal }) .then(response => { if (currentVersion !== this.requestVersion) return; this.totalPagesFetch = response.headers.get("X-WP-TotalPages"); return response.json(); }) .then(data => { if (!data || currentVersion !== this.requestVersion) return; const newid = data.map(item => item.id); const postUrl = newid.length === 0 ? `${this.searchbyid}0` : `${this.searchbyid}${newid.join(',')}`; return fetch(postUrl, { signal: controller.signal }); }) .then(res => res ? res.json() : []) .then(postData => { if (currentVersion !== this.requestVersion) return; this.blogs = postData; this.noData = postData.length === 0; }) .catch(err => { if (err.name !== 'AbortError') console.error(err); }); } } });
原有HTML结构无需改动,记得把之前全局定义的searchPagination和sleeper函数删除即可,这两部分逻辑已经整合到实例方法中,不再需要。
效果说明
修复后不管用户多快输入、删除关键词,页面永远只会展示最后一次操作对应的结果,不会再出现结果错配、闪烁、滞后的问题,同时无效请求量会下降80%以上,接口压力也会更小。
内容的提问来源于stack exchange,提问作者Marcuzio Developuzio
相关产品推荐
相关产品推荐

