背景
项目中有一个内容区域和tab栏,选中不同的tab选项展示对应的内容,如果内容由后端返回,前端发起两次请求,如果第二次请求先返回,则会先展示第二次请求的内容,最后展示第一次请求的内容。而用户期望的是最终展示最后一次选择的结果。因此,当发起第二次请求前,需要取消上一次请求。
复现步骤:
- 请求tab2的内容,3秒后返回
- 请求tab3的内容,立即返回
- 内容区域显示顺序: tab3的内容 -> tab2的内容
前端代码 github代码
- axios请求文件 src/util/request.ts
import axios from "axios";
const http = axios.create({
baseURL: "http://localhost:3000",
timeout: 5000,
});
// 获取tab内容
export const getTabContent = (index: number) => {
return http.get(`/tab/${index}`);
};
- 页面 src/App.vue
<template>
<div class="container">
<div class="btns">
<button v-for="i in 3" :key="i" @click="handleTabClick(i)" >{{ `tab${i}` }}</button>
</div>
<div class="context">{{ context }}</div>
</div>
</template>
<script setup lang="ts">
import { handleError, ref } from 'vue'
import { getTabContent } from '@/utils/request'
const context = ref('tab1的内容...')
const handleTabClick = async (index:number) => {
const { data } = await getTabContent(index)
context.value = data
}
</script>
后端代码 github代码
// app.ts
const express = require("express");
const app = express();
const cors = require("cors");
app.use(cors());
app.get("/tab/:id", (req, res) => {
if (req.params.id === "1") {
setTimeout(() => {
res.send("这是tab1的内容...");
}, 1000);
} else if (req.params.id === "2") {
setTimeout(() => {
res.send("这是tab2的内容...");
}, 3000);
} else if (req.params.id === "3") {
res.send("这是tab3的内容...");
}
});
app.listen("3000", () => {
console.log("server running at 3000 port...");
});
解决办法
AbortController
最新axios官方文档推荐的方法 文档地址 简而言之,new一个AbortController对象,将其signal属性作为参数传递给axios的请求方法中
- 优化后的request.ts
import axios from "axios";
const http = axios.create({
baseURL: "http://localhost:3000",
timeout: 5000,
});
let controller: any;
export const getTabContent = (index: number) => {
// 取消上一次请求
if (controller) {
controller.abort();
}
// 发起新的请求
controller = new AbortController();
return http.get(`/tab/${index}`, { signal: controller.signal });
};
最终效果
前一次请求被cancel,最终显示tab3的内容