axios 是一个基于 promise 的 HTTP 库,能够用在浏览器和 node.js 中。
在前端框架中的应用也是特别普遍,无论是vue仍是react,都有不少项目用axios做为网络请求库。
我在最近的几个项目中都有使用axios,并基于axios根据常见的业务场景封装了一个通用的request服务。前端
业务场景:vue
定义全局的默认配置node
axios.defaults.timeout = 10000 //超时取消请求 axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8' axios.defaults.baseURL = process.env.BASE_URL //挂载在process下的环境常量,在我另外一篇文章有详细说明
如何定义多环境常量react
自定义配置(很是见业务场景,仅做介绍)ios
// 建立实例时设置配置的默认值 var instance = axios.create({ baseURL: 'https://api.another.com' }); // 在实例已建立后修改默认值 instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;
优先级:自定义配置 > 默认配置git
请求拦截器及取消重复请求github
// 请求列表 const requestList = [] // 取消列表 const CancelToken = axios.CancelToken let sources = {} axios.interceptors.request.use((config) => { //将请求地址及参数做为一个完整的请求 const request = JSON.stringify(config.url) + JSON.stringify(config.data) config.cancelToken = new CancelToken((cancel) => { sources[request] = cancel }) //1.判断请求是否已存在请求列表,避免重复请求,将当前请求添加进请求列表数组; if(requestList.includes(request)){ sources[request]('取消重复请求') }else{ requestList.push(request) //2.请求开始,改变loading状态供加载动画使用 store.dispatch('changeGlobalState', {loading: true}) } //3.从store中获取token并添加到请求头供后端做权限校验 const token = store.getters.userInfo.token if (token) { config.headers.token = token } return config }, function (error) { return Promise.reject(error) })
1.请求拦截器中将全部请求添加进请求列表变量,为取消请求及loading状态管理作准备;当请求列表已存在当前请求则不响应该请求。
2.请求一旦开始,就能够开启动画加载效果。
3.用户登陆后能够在请求头中携带token作权限校验使用。json
响应拦截器axios
axios.interceptors.response.use(function (response) { // 1.将当前请求中请求列表中删除 const request = JSON.stringify(response.config.url) + JSON.stringify(response.config.data) requestList.splice(requestList.findIndex(item => item === request), 1) // 2.当请求列表为空时,更改loading状态 if (requestList.length === 0) { store.dispatch('changeGlobalState', {loading: false}) } // 3.统一处理权限认证错误管理 if (response.data.code === 900401) { window.ELEMENT.Message.error('认证失效,请从新登陆!', 1000) router.push('/login') } return response }, function (error) { // 4.处理取消请求 if (axios.isCancel(error)) { requestList.length = 0 store.dispatch('changeGlobalState', {loading: false}) throw new axios.Cancel('cancel request') } else { // 5.处理网络请求失败 window.ELEMENT.Message.error('网络请求失败', 1000) } return Promise.reject(error) })
1.响应返回后将相应的请求从请求列表中删除
2.当请求列表为空时,即全部请求结束,加载动画结束
3.权限认证报错统一拦截处理
4.取消请求的处理须要结合其余代码说明
5.因为项目后端并无采用RESTful风格的接口请求,200之外都归为网络请求失败segmentfault
const request = function (url, params, config, method) { return new Promise((resolve, reject) => { axios[method](url, params, Object.assign({}, config)).then(response => { resolve(response.data) }, err => { if (err.Cancel) { console.log(err) } else { reject(err) } }).catch(err => { reject(err) }) }) } const post = (url, params, config = {}) => { return request(url, params, config, 'post') } const get = (url, params, config = {}) => { return request(url, params, config, 'get') } //3.导出cancel token列表供全局路由守卫使用 export {sources, post, get}
1.axios cancel token API
2.存入须要取消的请求列表导出给导航守卫使用
3.路由发生变化的时候取消当前页面尚未返回结果的请求,在复杂的页面中请求可能会有不少个,增长取消请求的功能可让页面切换的时候不卡顿。
/src/router.js
... import { sources } from '../service/request' ... router.beforeEach((to, from, next) => { document.title = to.meta.title || to.name //路由发生变化时取消当前页面网络请求 Object.keys(sources).forEach(item => { sources[item]('取消前页面请求') }) for (var key in sources) { delete sources[key] } next() })
// 引入网络请求库 https://github.com/axios/axios import axios from 'axios' import store from '../store' import router from '../router' // 请求列表 const requestList = [] // 取消列表 const CancelToken = axios.CancelToken let sources = {} // axios.defaults.timeout = 10000 axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8' axios.defaults.baseURL = process.env.BASE_URL axios.interceptors.request.use((config) => { const request = JSON.stringify(config.url) + JSON.stringify(config.data) config.cancelToken = new CancelToken((cancel) => { sources[request] = cancel }) if(requestList.includes(request)){ sources[request]('取消重复请求') }else{ requestList.push(request) store.dispatch('changeGlobalState', {loading: true}) } const token = store.getters.userInfo.token if (token) { config.headers.token = token } return config }, function (error) { return Promise.reject(error) }) axios.interceptors.response.use(function (response) { const request = JSON.stringify(response.config.url) + JSON.stringify(response.config.data) requestList.splice(requestList.findIndex(item => item === request), 1) if (requestList.length === 0) { store.dispatch('changeGlobalState', {loading: false}) } if (response.data.code === 900401) { window.ELEMENT.Message.error('认证失效,请从新登陆!', 1000) router.push('/login') } return response }, function (error) { if (axios.isCancel(error)) { requestList.length = 0 store.dispatch('changeGlobalState', {loading: false}) throw new axios.Cancel('cancel request') } else { window.ELEMENT.Message.error('网络请求失败', 1000) } return Promise.reject(error) }) const request = function (url, params, config, method) { return new Promise((resolve, reject) => { axios[method](url, params, Object.assign({}, config)).then(response => { resolve(response.data) }, err => { if (err.Cancel) { console.log(err) } else { reject(err) } }).catch(err => { reject(err) }) }) } const post = (url, params, config = {}) => { return request(url, params, config, 'post') } const get = (url, params, config = {}) => { return request(url, params, config, 'get') } export {sources, post, get}
以上。