引言
代码要写在一个私有作用域中(闭包),防止变量污染,用window.xxx=xxx的方式向外暴露使用的方法axios,axios.get|options|delete|head|post|put方法要挂载到方法实例上,还有defaults(默认的配置)和interceptors(拦截器),通过整理默认config和自定义的config结合得到最后的config,通过这个配置结合原生的ajax实现axios
代码
(function(){
//因为每调用axios都要new一个对象调用constructor函数,在这个函数中要发送一个ajax请求
class NewAxios{
constructor(config){
//请求拦截器得到config,返回修改后的config,请求拦截器要是函数
let requestInter=axios.interceptors.request.pond[0]
//要判断请求拦截器是方法的时候才能执行传入config得到新的config
if(typeof requestInter==="function"){
config= requestInter(config)
}
this.config=config
this.isGet=/^(get|delete|options|head)$/i.test(config.method)
return this.init()
}
//初始化,发送ajax请求
init(){
let responseInter=axios.interceptors.response.pond
return new Promise((reslove,reject)=>{
let {method,withCredentials,timeout, validateStatus}=this.config
let xhr=new XMLHttpRequest()
this.setHeders(xhr)
xhr.withCredentials=.withCredentials
xhr.timeout=timeout
xhr.open(method, this.getUrl())
xhr.onreadystatechange=()=>{
//箭头函数保证内部的this是环境的this
let flag=validateStatus(xhr.status)
let responseRes=this. getResponse(flag,xhr)
if(flag){
if(xhr.readyState===4){
reslove( responseRes);
}
}else{
reject({
response: responseRes,
message:xhr.responseText
})
}
}
xhr.send(this.getSend())
}).then(...responseInter)
}
//得到open的第二个参数值
getUrl(){
let {isGet,config={baseURL,url,params}}=this
//post请求url或者没有params的get请求的url
url=baseURL+url
//是get请求,并且params不为null
if(isGet&¶ms){
let urlp=paramsSerializer(params)
url+=`${url.includes("?")?"&":"?"}${urlp}`
}
return url
}
getSend(){
//拿到config中的data和transformRequest通过这个方法拿到请求体中的数据
let {isGet,config={data,transformRequest}}=this
if(isGet){
return null
}
return transformRequest(data)
}
setHeders(xhr){
let {headers}=this.config
if(!headers||typeof headers!=="object")return
for(let k in headers){
if(!headers.hasOwnProperty(k))break
xhr.setRequestHeader(k,headers[k])
}
}
getResponse(flag,xhr){
let response={
status:xhr.status,
statusText:xhr.statusText,
request:xhr,
config:this.config,
headers:{},
data:xhr.responseText
}
if(flag){
response.data=JSON.parse(xhr.responseText)
}
//通过xhr.getAllRequestHeaders得到的结果是xxx:xxx空格/回车不是一个对象或者json格式的字符串
let headersStr=xhr.getAllResponseHeaders()
let headersArr=headersStr.split(/(\n|\t)/g)
headersArr.forEach(item=>{
if(!item)return
let arr=item.split(":")
response.header[arr[0]]=arr[1]
})
return response
}
}
//axios是个方法,也可当成对象添加get,post等属性
function paramsSerializer(obj){
if(!obj||typeof obj!=="object"){
return obj
}
let urlP=""
for(let k in obj){
//判断k是否为obj的私用属性
if(!obj.hasOwnProperty(k))break
urlP+=`&${k}=${obj[k]}`
}
urlP=urlP.substring(1)
return urlP
}
function axios(config){
//得到最后的config
config= newConfig(config)
//通过new 一newNewAxios对象调用constructor函数返回promise对象
let promiseObj=new NewAxios(config)
return promiseObj
}
//把默认的配置与新设置的配置结合
function newConfig(config){
if(config.hasOwnProperty("headers")){
//Object.assign是浅比较结合,所以对于headers这种对象来说就是直接替换不会把对象中的内容合并并覆盖
config.headers=Object.assign(axios.defaults.headers,config.headers)
}
let newConfig=Object.assign(axios.defaults,config)
return newConfig
}
//设置axios的拦截器,interceptor对象中request对象和response对象,中的use方法
function use(...params){
this.pond=params
}
axios.interceptors={
request:{
use,
pond:[]
},
response:{
use,
pond:[]
}
}
//设定axios的defautls属性用来社定默认的参数
axios.defaults={
baseURl:"",
url:"",
method:"get",
data:null,
params:null,
//当以post形式请求时,会调用这个方法传入data,得到的返回值就是请求体中的数据,请求体中的数据设置源自于xhr.send(请求体中的数据)
transformRequest:data=>{
if(!data||typeof data!=="object"){
return data
}
return JSON.stringify(data)
},
headers:{
//axios默认请求体中的数据格式是json格式字符串
"Content-Type":"application/json"
},
//是否允许跨域资源请求时携带资源凭证
withCredentials:false,
responseType: 'json',
timeout:0,
validateStatus:status=>{
return status>=200&& status<300
}
}
["get","delete","options","head"].forEach(item=>{
axios[item]=function(url="",config={}){
config.method=item
config.url=url
return axios(config)
}
})
["post","put"].forEach(item=>{
axios[item]=function(url="",data={},config={}){
config.method=item
config.url=url
config.data=data
return axios(config)
}
})
window._axios=axios
})()
_axios({
url:""
})
_axios.get(url,config对象)
复制代码
总结
通过promise和原生的ajax来封装axios,axios.defaults和自定义的config结合后的配置,用来设置原生ajax的一些需要的参数,定义一个NewAxios类,每当调用执行axios方法就要new一个NewAxios的实例,执行constructor构造函数传入config
(this.config=config挂载到实例上)=>执行init方法,在init方法中根据传近来的
config参数,发送ajax请求
config的整合
axios.defaults中是一些默认的配置项,在执行axios时会传入自定义的config,,要把自定义的config与默认的axios.defaults合并(都存在的config中覆盖defaults),使用Object.assign(axios.defaults,config),如果config中有对象(类似于headers),就要先把属性通过Object.assign整合,赋值给config.headers,之后再整体
Object.assign(axios.defaults,config),避免默认的headers被覆盖,返回一个最终使用的config
function newConfig(config){
if(config.hasOwnProperty("headers")){
//Object.assign是浅比较结合,所以对于headers这种对象来说就是直接替换不会把对象中的内容合并并覆盖
config.headers=Object.assign(axios.defaults.headers,config.headers)
}
let newConfig=Object.assign(axios.defaults,config)
return newConfig
}
复制代码
url的整合
根据config中的baseURl和url,得到基本的url;config中的method判断方式,post方式直接使用url,get系列方式在params不为null的情况下要把params对象转化成
xxx=xxx&yyy=yyy的形式,定义以下方法
当post请求头的Content-type为xx-www-form-urlencoded时请求体的数据格式也为
xxx=xxx&yyy=yyy,另外Qs.stringify()方法可以把对象转化为上述格式
function paramsSerializer(obj){
if(!obj||typeof obj!=="object"){
return obj
}
let urlP=""
for(let k in obj){
//判断k是否为obj的私用属性
if(!obj.hasOwnProperty(k))break
urlP+=`&${k}=${obj[k]}`
}
urlP=urlP.substring(1)
return urlP
}
复制代码
send整合
send是发送请求体内容的,get系列请求发送null,post系列请求,要发送
transformRequest方法的返回值
getSend(){
//拿到config中的data和transformRequest通过这个方法拿到请求体中的数据
let {isGet,config={data,transformRequest}}=this
if(isGet){
return null
}
return transformRequest(data)
}
复制代码
请求头的设置
核心应用xhr.setRequestHeader()方法
setHeders(xhr){
let {headers}=this.config
if(!headers||typeof headers!=="object")return
for(let k in headers){
if(!headers.hasOwnProperty(k))break
xhr.setRequestHeader(k,headers[k])
}
}
复制代码
整合响应后的信息
当响应体请求回来之后,要整合返回来的结果,把结果整合成一个对象,通过validateStatus来判断是否请求成功,如果得到的是true就是请求成功,xhr.responseText的到响应体的信息,如果请求失败就是失败的信息,请求成功就是json字符串,
通过JSON.parse(xhr.responseText)把它转化成对象,通过
xhr.getAllResponseHeaders()得到的响应头信息是字符串,通过split(/(\n|\t)/g)
把字符串切割得到元素为xxx:xxx的数组,遍历数组,通过split得到key和value
getResponse(flag,xhr){
let response={
status:xhr.status,
statusText:xhr.statusText,
request:xhr,
config:this.config,
headers:{},
data:xhr.responseText
}
if(flag){
response.data=JSON.parse(xhr.responseText)
}
//通过xhr.getAllRequestHeaders得到的结果是xxx:xxx空格/回车不是一个对象或者json格式的字符串
let headersStr=xhr.getAllResponseHeaders()
let headersArr=headersStr.split(/(\n|\t)/g)
headersArr.forEach(item=>{
if(!item)return
let arr=item.split(":")
response.header[arr[0]]=arr[1]
})
return response
}
复制代码
拦截器
拦截器是axios方法的属性interceptors对象中request和response对象每隔对象中都有use方法和pond事件池,每次执行响应对象的use方法都是在对应的时间池中添加方法,request拦截器是在config赋值给实例之前调用修改config的值,响应拦截器是在数据返回后在获取数据之前多加了一个then对返回数据的进一步而处理