一、什么是axios,有什么特性
描述
axios是一个基于promise
的HTTP
库,可以用在浏览器
或者node.js
中。本文围绕XHR。
axios提供两个http请求适配器,XHR和HTTP。XHR的核心是浏览器端的XMLHttpRequest对象;HTTP的核心是node的http.request方法。
特性:
从浏览器中创建XMLHttpRequests
从node.js创建http请求
支持promise API
拦截请求与响应
转换请求数据与响应数据
取消请求
自动转换JSON数据
客户端支持防御XSRF
背景
自Vue
2.0起,尤大宣布取消对vue-resource
的官方推荐,转而推荐axios
。现在axios
已经成为大部分Vue
开发者的首选,目前在github上有87.3k star。axios
的熟练使用和基本封装也成为了vue技术栈系列必不可少的一部分。如果你还不了解axios,建议先熟悉 axios官网文档。
基本使用
安装
npminstallaxios-S
使用
importaxiosfrom'axios'//为给定ID的user创建请求axios.get('/user?ID=12345').then(function(response){console.log(response);}).catch(function(error){console.log(error);});//上面的请求也可以这样做axios.get('/user',{params:{ID:12345}}).then(function(response){console.log(response);}).catch(function(error){console.log(error);});
二、Vue项目中为什么要封装axios
axios
的API很友好,可以在项目中直接使用。但是在大型项目中,http请求很多,且需要区分环境, 每个网络请求有相似需要处理的部分,如下,会导致代码冗余,破坏工程的可维护性
,扩展性
axios('http://www.kaifa.com/data',{//配置代码method:'GET',timeout:3000,withCredentials:true,headers:{'Content-Type':'application/json'},//其他请求配置...}).then((data)=>{//todo:真正业务逻辑代码console.log(data);},(err)=>{//错误处理代码if(err.response.status===401){//handleauthorizationerror}if(err.response.status===403){//handleserverforbiddenerror}//其他错误处理.....console.log(err);});
环境区分
请求头信息
请求类型
请求超时时间
timeout: 3000
允许携带cookie
withCredentials: true
响应结果处理
登录校验失败
无权限
成功
...
三、Vue项目中如何封装axios
axios文件封装在目录src/utils/https.js
,对外暴露callApi
函数
1、环境区分
callApi
函数暴露prefixUrl
参数,用来配置api url前缀
,默认值为api
//src/utils/https.jsimportaxiosfrom'axios'exportconstcallApi=({url,...prefixUrl='api'})=>{if(!url){consterror=newError('请传入url')returnPromise.reject(error)}constfullUrl=`/${prefixUrl}/${url}`...returnaxios({url:fullUrl,...})}
看到这里大家可能会问,为什么不用axios提供的配置参数baseURL
,原因是baseURL
会给每个接口都加上对应前缀,而项目实际场景中,存在一个前端工程,对应多个服务
的场景。需要通过不用的前缀代理到不同的服务,baseURL
虽然能实现,但是需要二级前缀,不优雅,且在使用的时候看不到真实的api地址是啥,因为代理前缀跟真实地址混合在一起了
使用baseURL
,效果如下
函数设置prefixUrl参数,效果如下
利用环境变量
及webpack代理
(这里用vuecli3配置)来作判断,用来区分开发、测试环境。生产环境同理配置nginx
代理
//vue.config.jsconsttargetApi1=process.env.NODE_ENV==='development'?"http://www.kaifa1.com":"http://www.ceshi1.com"consttargetApi2=process.env.NODE_ENV==='development'?"http://www.kaifa2.com":"http://www.ceshi2.com"module.exports={devServer:{proxy:{'/api1':{target:targetApi1,changeOrigin:true,pathRewrite:{'/api1':""}},'/api2':{target:targetApi2,changeOrigin:true,pathRewrite:{'/api2':""}},}}}
2、请求头
常见以下三种
(1)application/json
参数会直接放在请求体中,以JSON格式的发送到后端。这也是axios请求的默认方式。这种类型使用最为广泛。
//src/utils/https.jsimportaxiosfrom'axios'importqsfrom'qs'constcontentTypes={json:'application/json;charset=utf-8',urlencoded:'application/x-www-form-urlencoded;charset=utf-8',multipart:'multipart/form-data',}constdefaultOptions={headers:{Accept:'application/json','Content-Type':contentTypes.json,}}exportconstcallApi=({url,data={},options={},contentType='json',//json||urlencoded||multipartprefixUrl='api'})=>{...constnewOptions={...defaultOptions,...options,headers:{'Content-Type':options.headers&&options.headers['Content-Type']||contentTypes[contentType],},}const{method}=newOptionsif(method!=='get'&&method!=='head'){if(datainstanceofFormData){newOptions.data=datanewOptions.headers={'x-requested-with':'XMLHttpRequest','cache-control':'no-cache',}}elseif(options.headers['Content-Type']===contentTypes.urlencoded){newOptions.data=qs.stringify(data)}else{Object.keys(data).forEach((item)=>{if(data[item]===null||data[item]===undefined||data[item]===''){deletedata[item]}})//没有必要,因为axios会将JavaScript对象序列化为JSON//newOptions.data=JSON.stringify(data);}}returnaxios({url:fullUrl,...newOptions,})}
注意,在application/json
格式下,JSON.stringify处理传参没有意义,因为axios会将JavaScript对象序列化为JSON,也就说无论你转不转化都是JSON
3、请求类型
请求类型参数为axios
的options
的method
字段,传入对应的请求类型如post
、get
等即可
不封装,使用原生axios
时,发送带参数的get请求
如下:
//src/service/index.jsimport{callApi}from'@/utils/https';exportconstdelFile=(params)=>callApi({url:`file/delete?systemName=${params.systemName}&menuId=${params.menuId}&appSign=${params.appSign}`,option:{method:'get',},});//或者exportconstdelFile=(params)=>callApi({url:'file/delete',option:{method:'get',params},});
官方文档如下
callApi
函数暴露method
参数,用来配置请求类型
,默认值为get
当请求类型为get
时,将callApi
函数暴露的data
参数,设置为options.params
,从而参数自动拼接到url地址之后
//src/utils/https.jsimportaxiosfrom'axios'exportconstcallApi=({url,data={},method='get',options={},...prefixUrl='api'})=>{...constnewOptions={...,...options,method}...if(method==='get'){newOptions.params=data}...returnaxios({url:fullUrl,...newOptions,})}
4、请求超时时间
//src/utils/https.jsconstdefaultOptions={timeout:15000,}
5、允许携带cookie
//src/utils/https.jsconstdefaultOptions={withCredentials:true,}
6、响应结果处理
通过.then
、.catch()
处理
这块需要跟服务端约定接口响应全局码
,从而统一处理登录校验失败
,无权限
,成功
等结果
比如有些服务端对于登录校验失败
,无权限
,成功
等返回的响应码都是200,在响应体内返回的状态码分别是20001,20002,10000,在then()
中处理
比如有些服务端对于登录校验失败
,无权限
,成功
响应码返回401,403,200,在catch()
中处理
importaxiosfrom'axios'//为给定ID的user创建请求axios.get('/user?ID=12345').then(function(response){console.log(response);}).catch(function(error){console.log(error);});//上面的请求也可以这样做axios.get('/user',{params:{ID:12345}}).then(function(response){console.log(response);}).catch(function(error){console.log(error);});0
上述方案在Message.error(xx)
时,当多个接口返回的错误信息一致时,会存在重复提示
的问题,如下图
优化方案,利用防抖
,实现错误提示一次,更优雅
四、完整封装及具体使用
代码可访问本人的github
axios-ajax完整封装
importaxiosfrom'axios'//为给定ID的user创建请求axios.get('/user?ID=12345').then(function(response){console.log(response);}).catch(function(error){console.log(error);});//上面的请求也可以这样做axios.get('/user',{params:{ID:12345}}).then(function(response){console.log(response);}).catch(function(error){console.log(error);});1
importaxiosfrom'axios'//为给定ID的user创建请求axios.get('/user?ID=12345').then(function(response){console.log(response);}).catch(function(error){console.log(error);});//上面的请求也可以这样做axios.get('/user',{params:{ID:12345}}).then(function(response){console.log(response);}).catch(function(error){console.log(error);});2
具体使用
api管理文件在目录src/service
下,index.js
文件暴露其他模块,其他文件按功能模块划分
文件
get请求带参数 自定义前缀代理不同服务 文件类型处理
五、总结
axios
封装没有一个绝对的标准,且需要结合项目中实际场景
来设计,但是毋庸置疑,axios-ajax的封装是非常有必要的。
原文地址:https://github.com/zxyue25/axios-ajax
其他文章:vue多工程间公共模块处理最佳实践
本文收录专栏【业务总结】旨在沉淀工作中遇到的问题,总结为最佳实践,欢迎关注✨