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

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章