近期使用uni-app开发的项目中出现一个问题,在发起请求后如果用户直接点击的后退按钮,那么之前页面的请求还会继续加载,并且成功的时候依然会执行success的逻辑。
比如先从A页面跳转到B页面,然后在B页面查询数据,正常情况是查询成功后跳转到一个新的C页面,如果在请求成功之前用户不想去做后面的操作了,直接点击了浏览器的回退按钮,那么回退到A页面之后B页面的请求依然会继续执行,并且请求成功以后success的逻辑也会执行,会导致从A页面直接跳转的C页面。
最好的解决办法可能是①在离开一个页面后可以直接清空该页面组件的相关逻辑,并且停止页面内的请求?我找了一下没有看到这样的配置或者文档,所以不确定是否能够实现,并且如果有的页面逻辑是②:B页面同时发起两个请求task1,task2,在task1请求成功时会跳转页面,而task2的请求只负责传递数据,不做页面交互,那么在task1请求成功后页面跳转,B页面组件卸载直接停止task2请求的话可能也不合适。所以目前比较适合且可以实现的方式可能是在某些情况下手动停止请求。
关于uni.request停止请求的方法可以查看uni.request
直接使用uni.request的abort方法可以满足上方需求,但是写起来会比较麻烦,每一次请求都需要去保存对应的requestTask,在需要的时候再调用requestTask的abort方法。所以为了简化多请求的取消操作/写法,所以封装了一个方法。
我们的项目之前已经封装了uni.request方法,提供了自己的一些默认参数、请求头、返回数据处理以及错误提示等逻辑,这次在已有的基础上多加了取消请求的相关逻辑。这里我的思路是在发起请求时将当前requestTask作为一个对象的值存起来,对应的键可以是随机数或者guid格式的字符串,在请求成功或者失败后从对象中删除该键;当需要取消请求时把对象内的所有requestTask全部调用一次abort方法,以此取消请求。考虑到②的情况,所以暴露出去的request方法可以提供一个参数用于标识该请求是否可以被取消。而如果是希望实现①的情况的话,也可以监听路由变化,或者使用路由守卫,在页面变更时调用统一的取消请求方法。
import Vue from 'vue';
import { guid } from './utils.js';
let canCancelRequestObj = {};
const codeMessage = {
200: '服务器成功返回请求的数据。',
201: '新建或修改数据成功。',
202: '一个请求已经进入后台排队(异步任务)。',
204: '删除数据成功。',
400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
401: '用户没有权限(令牌过期)。',
403: '用户得到授权,但是访问是被禁止的。',
404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
406: '请求的格式不可得。',
410: '请求的资源被永久删除,且不会再得到的。',
422: '当创建一个对象时,发生一个验证错误。',
500: '服务器发生错误,请检查服务器。',
502: '网关错误。',
503: '服务不可用,服务器暂时过载或维护。',
504: '网关超时。',
};
function checkStatus(response) {
if (response.status >= 200 && response.status < 300) {
return response;
}
const errortext = codeMessage[response.status] || response.statusText;
//校验不通过,则
const error = new Error(errortext);
error.name = response.status;
error.response = response;
throw error;
}
/**
- Requests a URL, returning a promise.
- @param {object} [options] The options we want to pass to "request"
-
@return {object} An object containing either "data" or "err"
*/
export default function request(options,canCancel=false) {
// const defaultOptions = {
// credentials: 'include',
// };
let requestId = guid();
console.log('canCancelRequestObj:',canCancelRequestObj);
if(!options.url){
console.log('无请求url')
return
}
const newOptions = {
// ...defaultOptions,
...options,
success(res) {
delete canCancelRequestObj[requestId];
// console.log('response:'+JSON.stringify(res));
if(res.statusCode == '401' || (res.statusCode != '200'&&!res.data)){
uni.hideLoading();
let strResult='';
//清空登陆信息,跳转到登陆页
console.log('登陆信息失效')
if(res.data){
strResult = res.data;
}else{
strResult = res.errMsg;
}
let jsondata = {
result: strResult
};
Vue.prototype && Vue.prototype.$bridge && Vue.prototype.$bridge.callhandler&&Vue.prototype.$bridge.callhandler('jsCallNative', {
action: 'goLogin',
data: JSON.stringify(jsondata)
}, (data) => {}); return } options.success&&options.success(res.data)
},
fail(error) {
if(error&&error.errMsg&&error.errMsg.includes('fail abort')){
console.log(error)
//主动取消的请求
return
}else{
options.fail&&options.fail(error)
}
}
};
let Authorization = window.sessionStorage.getItem('ACCESS_TOKEN');
newOptions.header = {
Authorization:Authorization,
from:'PSM_ANDROID',
timestamp:new Date().getTime(),
};
switch (uni.getSystemInfoSync().platform) {
case 'android':
newOptions.header.from = 'PSM_ANDROID';
break;
case 'ios':
newOptions.header.from = 'PSM_APPLE';
break;
default:
newOptions.header.from = 'PSM_APPLE';break;
}
if (newOptions.method === 'POST' || newOptions.method === 'PUT' || newOptions.method === 'DELETE') {
newOptions.header = {
// Accept: 'application/json',
'content-type': 'application/json;',
...newOptions.header,
};
}
console.log(JSON.stringify(newOptions));
let requestTask = uni.request(newOptions);
if(canCancel){
canCancelRequestObj[requestId] = requestTask;
}
return requestTask;
}
export function cancelRequest (){
console.log(canCancelRequestObj);
if(!canCancelRequestObj||typeof canCancelRequestObj != 'object'){
return
}
Object.keys(canCancelRequestObj).forEach(item=>canCancelRequestObj[item].abort());
}
2 个评论
要回复文章请先登录或注册
2***@qq.com
1***@qq.com