Open libin1991 opened 6 years ago
我们在使用axios进行异步操作时,可能会遇到以下情况:
axios.post(url, data).then(res=>{}).catch(err=>{}) 复制代码
// 添加请求拦截器 axios.interceptors.request.use(function (config) { // 在发送请求之前做些什么 return config; }, function (error) { // 对请求错误做些什么 return Promise.reject(error); }); // 添加响应拦截器 axios.interceptors.response.use(function (response) { // 对响应数据做点什么 return response; }, function (error) { // 对响应错误做点什么 return Promise.reject(error); }); 复制代码
var CancelToken = axios.CancelToken; var cancel; axios.get('/user/12345', { cancelToken: new CancelToken(function executor(c) { // executor 函数接收一个 cancel 函数作为参数 cancel = c; }) }); // 取消请求 cancel(); 复制代码
axios.js 设置一个cancelFlag作为标志符,默认为true,在请求拦截器时,判断如果cancelFlag为true,就可以发送请求,且将cancelFlag设为false。当cancelFlag为false,就取消请求。在响应拦截器中再将cancelFlag设为true。说明只用当一个请求发送且收到响应后,才可以发送另一个请求。这里存在的问题:cancelFlag是全局变量,这样多页面多个接口请求时,互相会有影响这里的解决办法就是在axios.js中构建构造函数,这样可以让cancelFlag私有化,但是这样的方式会导致占有大量内存。 参考同事写的代码(版本二),我觉得非常的有道理。而且比较简单。 版本一、使用cancelFlag全局变量判断
import Vue from 'vue' import axios from 'axios' import {Indicator} from 'mint-ui' Vue.component(Indicator) let CancelToken = axios.CancelToken //取消请求 let cancelFlag = true //设置公共部分,请求头和超时时间 axios.defaults.headers = { 'X-Requested-With': 'XMLHttpRequest' } axios.defaults.timeout = 20000 //在请求拦截器时 axios.interceptors.request.use(config => { if (cancelFlag) { cancelFlag = false Indicator.open() } else { cancelToken: new CancelToken (c => { cancel = c }) cancel() } return config }, error => { return Promise.reject(error) }) axios.interceptors.response.use(config => { cancelFlag = true Indicator.close() return config }, error => { // }) 复制代码
版本二、异步请求时,带上一个参数requestName。 这里一开始的疑惑是,当请求a带上参数requestName后,发送多次请求,判断axios[requestName]和axios[requestName].cancel存在时,会做取消处理。那发送成功后,再点击时,axios[requestName]和axios[requestName].cancel还是会存在啊。这样还是会执行axios[requestName].cancel()。 这里是因为当上一次请求发送成功后,其axios[requestName].cancel这个方法已经失效,即使执行了这个方法也不起作用。axios[requestName].cancel的值永远是上一次的请求的取消回调。当上一次请求成功后,该回调会失效。
axios.interceptors.request.use(config => { let requestName = config.data.requestName if (requestName) { if (axios[requestName] && axios[requestName].cancel) { axios[requestName].cancel() } config.cancelToken = new CancelToken (c => { axios[requestName] = {} axios[requestName].cancel = c }) } return config }, error => { return Promise.reject(error) }) 复制代码
响应的错误处理封装
axios.interceptors.response.use(config => { Indicator.close() return config }, error => { cancelFlag = true Indicator.close() if (error && error.response) { switch (error.response.status) { case 400: error.message = '错误请求' break; case 401: error.message = '未授权,请重新登录' break; case 403: error.message = '拒绝访问' break; case 404: error.message = '请求错误,未找到该资源' break; case 405: error.message = '请求方法未允许' break; case 408: error.message = '请求超时' break; case 500: error.message = '服务器端出错' break; case 501: error.message = '网络未实现' break; case 502: error.message = '网络错误' break; case 503: error.message = '服务不可用' break; case 504: error.message = '网络超时' break; case 505: error.message = 'http版本不支持该请求' break; default: error.message = `连接错误${error.response.status}` } } else { error.message = "连接到服务器失败" } return Promise.reject(error.message) }) 复制代码
http.js(封装了post和get请求) 在axios.js文件里对响应拦截器做了判断error.response.status的值的处理,根据不同的状态码返回不同的error说明。在http.js文件里post和get函数的参数为三个,第三个参数error就是出现错误时的文案。使用该api可以自己设置该文案,如果不传这个参数,那么就返回axios.js设置的error文案
import Vue from 'vue' import axios from './axios' import 'mint-ui/lib/style.css'; import {Toast} from 'mint-ui' Vue.component(Toast) export function post (url, data, error) { return new Promise((resolve, reject) => { axios.post(url, data).then(res => { resolve(res) }, err => { err = error ? error : err Toast({ message: err, duration: 500 }) }) }) } export function get (url, data, error) { return new Promise((resolve, reject) => { axios.post(url, { data: data }).then(res => { resolve(res) }, err => { err = error ? error : err Toast({ message: err, duration: 500 }) }) }) } 复制代码
1)在main.js引入文件
import axios from '../utils/axios.js' import {post, get} from '../utils/http.js' Vue.prototype.$axios = axios Vue.prototype.$post = post Vue.prototype.$get = get 复制代码
2)组件中使用
this.$post('/api/saveInfo', { value: this.value, requestName: 'name01' }, '请求失败啦~~~').then(res => { // alert(res.data) }) 复制代码
代码:github地址链接
封装的必要性
我们在使用axios进行异步操作时,可能会遇到以下情况:
这里我们发现我们每一次写的时候,都需要写.catch(err=>{}),会造成代码的冗余axios.post(url, data).then(res=>{}).catch(err=>{}) 复制代码
封装过程
拦截器科普
// 添加请求拦截器 axios.interceptors.request.use(function (config) { // 在发送请求之前做些什么 return config; }, function (error) { // 对请求错误做些什么 return Promise.reject(error); }); // 添加响应拦截器 axios.interceptors.response.use(function (response) { // 对响应数据做点什么 return response; }, function (error) { // 对响应错误做点什么 return Promise.reject(error); }); 复制代码
取消请求处理方法科普
var CancelToken = axios.CancelToken; var cancel; axios.get('/user/12345', { cancelToken: new CancelToken(function executor(c) { // executor 函数接收一个 cancel 函数作为参数 cancel = c; }) }); // 取消请求 cancel(); 复制代码
本文代码
axios.js
设置一个cancelFlag作为标志符,默认为true,在请求拦截器时,判断如果cancelFlag为true,就可以发送请求,且将cancelFlag设为false。当cancelFlag为false,就取消请求。在响应拦截器中再将cancelFlag设为true。说明只用当一个请求发送且收到响应后,才可以发送另一个请求。这里存在的问题:cancelFlag是全局变量,这样多页面多个接口请求时,互相会有影响这里的解决办法就是在axios.js中构建构造函数,这样可以让cancelFlag私有化,但是这样的方式会导致占有大量内存。
参考同事写的代码(版本二),我觉得非常的有道理。而且比较简单。
版本一、使用cancelFlag全局变量判断
import Vue from 'vue' import axios from 'axios' import {Indicator} from 'mint-ui' Vue.component(Indicator) let CancelToken = axios.CancelToken //取消请求 let cancelFlag = true //设置公共部分,请求头和超时时间 axios.defaults.headers = { 'X-Requested-With': 'XMLHttpRequest' } axios.defaults.timeout = 20000 //在请求拦截器时 axios.interceptors.request.use(config => { if (cancelFlag) { cancelFlag = false Indicator.open() } else { cancelToken: new CancelToken (c => { cancel = c }) cancel() } return config }, error => { return Promise.reject(error) }) axios.interceptors.response.use(config => { cancelFlag = true Indicator.close() return config }, error => { // }) 复制代码
版本二、异步请求时,带上一个参数requestName。
这里一开始的疑惑是,当请求a带上参数requestName后,发送多次请求,判断axios[requestName]和axios[requestName].cancel存在时,会做取消处理。那发送成功后,再点击时,axios[requestName]和axios[requestName].cancel还是会存在啊。这样还是会执行axios[requestName].cancel()。
这里是因为当上一次请求发送成功后,其axios[requestName].cancel这个方法已经失效,即使执行了这个方法也不起作用。axios[requestName].cancel的值永远是上一次的请求的取消回调。当上一次请求成功后,该回调会失效。
axios.interceptors.request.use(config => { let requestName = config.data.requestName if (requestName) { if (axios[requestName] && axios[requestName].cancel) { axios[requestName].cancel() } config.cancelToken = new CancelToken (c => { axios[requestName] = {} axios[requestName].cancel = c }) } return config }, error => { return Promise.reject(error) }) 复制代码
响应的错误处理封装
axios.interceptors.response.use(config => { Indicator.close() return config }, error => { cancelFlag = true Indicator.close() if (error && error.response) { switch (error.response.status) { case 400: error.message = '错误请求' break; case 401: error.message = '未授权,请重新登录' break; case 403: error.message = '拒绝访问' break; case 404: error.message = '请求错误,未找到该资源' break; case 405: error.message = '请求方法未允许' break; case 408: error.message = '请求超时' break; case 500: error.message = '服务器端出错' break; case 501: error.message = '网络未实现' break; case 502: error.message = '网络错误' break; case 503: error.message = '服务不可用' break; case 504: error.message = '网络超时' break; case 505: error.message = 'http版本不支持该请求' break; default: error.message = `连接错误${error.response.status}` } } else { error.message = "连接到服务器失败" } return Promise.reject(error.message) }) 复制代码
http.js(封装了post和get请求)
在axios.js文件里对响应拦截器做了判断error.response.status的值的处理,根据不同的状态码返回不同的error说明。在http.js文件里post和get函数的参数为三个,第三个参数error就是出现错误时的文案。使用该api可以自己设置该文案,如果不传这个参数,那么就返回axios.js设置的error文案
import Vue from 'vue' import axios from './axios' import 'mint-ui/lib/style.css'; import {Toast} from 'mint-ui' Vue.component(Toast) export function post (url, data, error) { return new Promise((resolve, reject) => { axios.post(url, data).then(res => { resolve(res) }, err => { err = error ? error : err Toast({ message: err, duration: 500 }) }) }) } export function get (url, data, error) { return new Promise((resolve, reject) => { axios.post(url, { data: data }).then(res => { resolve(res) }, err => { err = error ? error : err Toast({ message: err, duration: 500 }) }) }) } 复制代码
使用
1)在main.js引入文件
import axios from '../utils/axios.js' import {post, get} from '../utils/http.js' Vue.prototype.$axios = axios Vue.prototype.$post = post Vue.prototype.$get = get 复制代码
2)组件中使用
this.$post('/api/saveInfo', { value: this.value, requestName: 'name01' }, '请求失败啦~~~').then(res => { // alert(res.data) }) 复制代码
代码:github地址链接