赞
踩
有两个tab:tab A 和tab B。现在点击A会发送A请求,点击B会发送B请求。如果用户点击A之后快速点击B,A请求比B请求先返回,那么页面就会错误渲染A。AB请求返回的数据结果完全一致,只是内容不同,在这种情况下,如何显示B请求的数据而不是A请求的数据。
本地服务器准备
const http = require('http') const url = require('url') const server = http.createServer() server.on('request', (req, res) => { console.log('Someone visit our web server') const method = req.method const URL = req.url console.log(`Your request url is ${URL}, and request method is ${method}`) if (req.method === 'OPTIONS') { console.log('OPTIONS') res.writeHead(200, { 'Access-Control-Allow-Origin': 'http://127.0.0.1:8080', 'Access-Control-Allow-Headers': 'Content-Type, Authorization, Content-Length, X-Requested-With, x-custom-header', 'Access-Control-Allow-Methods': 'PUT, POST, GET, DELETE, OPTIONS', 'Access-Control-Allow-Credentials': 'true' }) res.end() return // 确保在 OPTIONS 请求中及时返回,不再继续处理请求 } if (method === 'GET') { const { query } = url.parse(URL) const params = {} if (query.includes('&')) { query.split('&').forEach(item => { const queryPair = item.split('=') params[queryPair[0]] = queryPair[1] }) } else { const queryPair = query.split('=') params[queryPair[0]] = queryPair[1] } const tab = params['tab'] // 设置跨域响应头 res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': 'http://127.0.0.1:8080', 'Access-Control-Allow-Headers': 'Content-Type, Authorization, Content-Length, X-Requested-With', 'Access-Control-Allow-Methods': 'PUT, POST, GET, DELETE, OPTIONS', 'Access-Control-Allow-Credentials': 'true' // 添加此行 }) // 把内容发送给客户端 res.end(JSON.stringify(`Now it is the content of tab ${tab}`)) } }) server.listen(8081, () => { console.log('server running at http://127.0.0.1:3000') })
HTML文件准备
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>cancelDemo</title> <style> .box { position: relative; margin: 0 auto; margin-top: 60px; width: 400px; height: 540px; border: 2px solid #000; border-radius: 10px; } .box .content { margin: 20px; width: 360px; height: 400px; border: 2px solid #efefef; border-radius: 10px; text-align: center; } .box .tab { position: absolute; display: flex; justify-content: space-evenly; align-items: center; bottom: 0; width: 100%; height: 60px; border-top: 2px solid #9a9da3; border-radius: 5px; } .box .tab .btn { padding: 10px 20px; height: 40px; background-color: #4479e1; box-sizing: border-box; border-radius: 10px; } </style> </head> <body> <div class="box"> <div class="content">Now it is the content of tab 1</div> <div class="tab"> <div class="btn" data-index="1">tab 1 </div> <div class="btn" data-index="2">tab 2 </div> <div class="btn" data-index="3">tab 3 </div> </div> </div> <!-- <script src="./index.js" type="module"></script> --> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> <script type="module" src="./index.js"></script> </body> </html>
index.js文件准备
const tab = document.querySelector('.tab')
tab.addEventListener('click', e => {
if (e.target.className !== 'btn') return
const index = e.target.dataset.index
axios({
method: 'GET',
url: `http://127.0.0.1:3000/index?tab=${index}`
}).then(res => {
document.querySelector('.content').innerHTML = res.data
})
})
Axios官方提供了两种取消请求的方法:
CancelToken.source
工厂方法创建一个 cancel tokenconst CancelToken = axios.CancelToken; const source = CancelToken.source(); axios.get('/user/12345', { cancelToken: source.token }).catch(function (thrown) { if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { // 处理错误 } }); axios.post('/user/12345', { name: 'new name' }, { cancelToken: source.token }) // 取消请求(message 参数是可选的) source.cancel('Operation canceled by the user.');
CancelToken
的构造函数来创建一个 cancel tokenconst CancelToken = axios.CancelToken;
let cancel;
axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
// executor 函数接收一个 cancel 函数作为参数
cancel = c;
})
});
// 取消请求
cancel();
const controller = new AbortController();
axios.get('/foo/bar', {
signal: controller.signal
}).then(function(response) {
//...
});
// 取消请求
controller.abort()
先对axios进行二次封装,这里简单地设置baseURL和timeout。下面是request中的index.js
// request/index.js
export const server = axios.create({
baseURL: 'http://127.0.0.1:8081/',
timeout: 3000,
headers: {
'X-Custom-Header': 'foobar',
}
})
再对获取tab信息的api进行封装:
// request/tab.js
import {server} from './index.js'
// 切换tab的请求
export const getTabContentAPI = async (index, cfg) => {
cfg = cfg || {}
return await server.get(`index?tab=${index}`, cfg)
}
对tab请求增加取消功能时,需要在调用api时,加上cancelToken选项
import { getTabContentAPI } from "./request/tab.js" const tab = document.querySelector('.tab') const CancelToken = axios.CancelToken let cancelFn const getTabContent = async index => { if (index == 3) cancelFn(`取消了tab${index}的上一个请求`) const res = await getTabContentAPI(index, { cancelToken: new CancelToken(c => cancelFn = c) }) return res } tab.addEventListener('click', async e => { if (e.target.className !== 'btn') return const index = e.target.dataset.index const res = await getTabContent(index) document.querySelector('.content').innerHTML = res.data })
但这样只能做到取消tab3的前一个请求,无法灵活地做到取消每一个tab的前一个请求。同时如果直接使用 cancelFn(
取消了tab${index}的上一个请求)
,就会导致首次发送请求时, cancelFn 还不是function。所以我们可以用一个数组来保存cancelFn,同时使用try catch
// index.js import { getTabContentAPI } from "./request/tab.js" const tab = document.querySelector('.tab') const CancelToken = axios.CancelToken const isCancel = axios.isCancel let cancelFnArr = [] const getTabContent = async index => { if (cancelFnArr.length > 0) { cancelFnArr.pop()(`取消了tab${index}的上一个请求`) } const res = await getTabContentAPI(index, { cancelToken: new CancelToken(c => cancelFnArr.push(c)) }) return res } tab.addEventListener('click', async e => { if (e.target.className !== 'btn') return const index = e.target.dataset.index try { const res = await getTabContent(index) document.querySelector('.content').innerHTML = res.data } catch(err) { if (isCancel(err)) { console.log('请求被取消', err.message) } else { console.log('请求出错', err.message) } } })
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。