axios切换路由取消指定请求与取消重复请求并存方案

前言

关于axios取消请求的文章和资料不胜其数。大部分都是单独介绍 切换路由时取消上个页面还没有完成的请求 或者 取消重复请求 的文章,且大部分是介绍了核心的内容,缺少关于一整套完整的方案的描述。javascript

此文章介绍这么一套完整方案,便可知足切换路由取消指定请求取消重复请求,并附带一些其余小干货处理前端

请求拦截

关于axios取消请求,官方文档也有简单描述到使用方法,基础方法我这里不赘述,直接写方案。vue

思路:java

用一个变量存储目前处于pending状态的请求,用一个标识代表。拦截发送请求,判断这个api请求以前是否已经有还在pending的同类,便是否存在上述变量中,若是存在,则取消处理,不存在就正常发送,等请求完结后删除这个api请求在上述变量中的标识,这是一个完整的处理取消重复请求的流程。ios

而对于切换了路由(页面),要取消上个页面仍然pending的请求,就须要监听路由切换,每次切换都对上述变量存储的请求标识作判断,哪些是要取消的请求,就给取消掉便可。这里并无对全部处于pending状态的请求作取消处理,缘由是,系统中可能有些请求是没必要要因为切换路由就要取消的,如全局的一些请求等。固然,这个也能够延伸一下,用于你实际项目需求,指定哪些所须要取消。vue-router

关于请求效果的设置,我习惯单独一个文件http.js,代码注释一步一步写得很清楚,你们看下去就天然很好理解了。axios

// http.js

import axios from 'axios';

// 用于存储目前状态为pending的请求标识信息
let pendingRequest = [];

/** * 请求的拦截处理 * @param config - 请求的配置项 */
const handleRequestIntercept = config => {
    // 区别请求的惟一标识,这里用方法名+请求路径
    // 若是一个项目里有多个不一样baseURL的请求
    // 能够改为`${config.method} ${config.baseURL}${config.url}`
    const requestMark = `${config.method} ${config.url}`;
    // 找当前请求的标识是否存在pendingRequest中,便是否重复请求了
    const markIndex = pendingRequest.findIndex(item => {
        return item.name === requestMark;
    });
    // 存在,即重复了
    if (markIndex > -1) {
        // 取消上个重复的请求
        pendingRequest[markIndex].cancel();
        // 删掉在pendingRequest中的请求标识
        pendingRequest.splice(markIndex, 1);
    }
    // (从新)新建针对此次请求的axios的cancelToken标识
    const CancelToken = axios.CancelToken;
    const source = CancelToken.source();
    config.cancelToken = source.token;
    // 设置自定义配置requestMark项,主要用于响应拦截中
    config.requestMark = requestMark;
    // 记录本次请求的标识
    pendingRequest.push({
        name: requestMark,
        cancel: source.cancel,
        routeChangeCancel: config.routeChangeCancel // 可能会有优先级高于默认设置的routeChangeCancel项值
    });
    
    return config;
};

/** * 响应的拦截处理 * @param config - 请求的配置项 */
const handleResponseIntercept = config => {
    // 根据请求拦截里设置的requestMark配置来寻找对应pendingRequest里对应的请求标识
    const markIndex = pendingRequest.findIndex(item => {
        return item.name === config.requestMark;
    });
    // 找到了就删除该标识
    markIndex > -1 && pendingRequest.splice(markIndex, 1);
}

/** * 建立axios实例 * @param {String} url - 访问后台的主url */
const createAxiosInstance = (baseUrl) => {
    let instance = axios.create({
        baseURL: baseUrl
    });
    // 默认把请求视为切换路由就会把pending状态的请求取消,false为不取消
    instance.defaults.routeChangeCancel = true;
    
    // 请求拦截
    instance.interceptors.reqeust.use(handleRequestIntercept, error => Promise.reject(error));
    
    // 响应拦截
    instance.interceptors.response.use(res => {
        handleResponseIntercept(res.config);
        // 其实更多状况下你执行获取res.data
        // 能够return res.data;
        return res;
    }, error => {
        let errorFormat = {};
        const response = error.response;
        // 请求已发出,但服务器响应的状态码不在 2xx 范围内
        if (response) {
            handleResponseIntercept(response.config);
            // 设置返回的错误对象格式(按照本身项目实际需求)
            let errorFormat = {
                status: response.status,
                data: response.data
            };
        }
        // 若是是主动取消了请求,作个标识
        if (axios.isCancel(error)) {
            errorFormat.selfCancel = true;
        }
        // 其实还有一个状况
        // 在设置引起错误的请求时,error.message才是错误信息
        // 但我以为这个通常是脚本错误,咱们项目提示也不该该提示脚本错误给用户看,通常都是咱们自定义一些默认错误提示,如“建立成功!”
        // 因此这里不针对此状况作处理。
        
        return Promise.reject(errorFormat);
    });
    
    // 还有一些其余你想要的axios实例设置
    // ...
    
    return instance;
}

// 其余配置
// ...

export {
    pendingRequest
}
复制代码

上面的代码里就能实现取消重复请求了,那么要实现切换路由后取消pending的指定请求,就须要在监听路由的变化了。api

在路由设置文件里,这里以vue-router为例子性能优化

// router.js

import { pendingRequest } from 'http.js';

// ...这是其余配置

router.beforeEach((to, from, next) => {
    // 把上个页面还没结束的请求取消掉
    pendingRequest.forEach(item => {
        item.routeChangeCancel && item.cancel();
    });
    // ... 其余处理
});
复制代码

实际项目应用时

处理报错

上面咱们定好了怎么作拦截,怎么处理路由切换,基本上已经处理好了。bash

还有一些小点,还记得咱们对响应拦截作了判断是不是主动取消,而后设置了selfCancel标识。

这个有什么用呢?是用在咱们在项目中发请求时,捕获错误信息时作处理的。如

// 我建立了axios实例并绑在Vue的http上
Vue.http.get('/api/test').then(res => {
    // 成功请求的处理
}).catch(e => {
    // 这里就要判断是不是主动取消请求的
    e.selfCancel || this.$message.error('请求失败!');
})
复制代码

这里的this.$message.error主要是用来提示错误信息给用户看的,而因为咱们主动取消了请求,会走到catch流程中,可是这实际意义上并不算一个错误,不该该告诉用户出现错误了。

因此这个selfCancel标识就是用来告诉开发者这是咱们主动取消请求致使的报错,不该该提示给用户看

区分是否页面请求

咱们上面经过请求的config.routeChangeCancel来判断切换路由时是否取消正在pending的请求。

咱们是统一在建立axios实例时,就设置默认routeChangeCancel为true,通常什么状况下是true呢? 咱们切换路由每每就是想把上个页面还没有完成的请求给取消掉,避免因为切换了路由请求完成了回调函数处理时若是有涉及上个页面的变化或方法或dom对象等,如今没有了会报脚本错等问题。固然这也是个性能优化的处理,提高用户交互体验,因此这个是有必要的。

有的人可能以为,只要切换了路由就把目前全部还没有完成的请求取消不就行了吗?可是你的系统里或许有些公共请求,不受且不该受页面的切换而受到影响的请求,这类请求就不该该在切换路由的时候给取消掉了!常见的例子是位于系统头部的某些信息是须要发请求获取的,切换页面头部header也是不会变的对吧。

那么咱们已经经过instance.defaults.routeChangeCancel = true设置了,那么接下来怎么设置才能知道哪些请求不须要切换路由就取消呢?

Vue.http.get('/api/test', {
    routeChangeCancel: false
})
复制代码

在实例发送请求的时候,这么设置就能覆盖了

其实若是不想以这种设置的形式去区分,其实也能够建立两种axios实例,一种表示切换路由就要取消的,另外一种就是不须要取消的。可是我以为,这种不须要取消的请求在一个项目中,一个系统中较少,其实经过这种配置的形式灵活性更好,处理起来也不会不少很麻烦。

总结

关于取消请求的场景就介绍到这里了。

未经容许,请勿私自转载

始发于 K前端

相关文章
相关标签/搜索