ios 原生ajax请求,基于promise和原生ajax写axios源码

引言

代码要写在一个私有作用域中(闭包),防止变量污染,用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&&params){

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对返回数据的进一步而处理

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值