axios的手动封装之准备工作

2,679 阅读6分钟
axios是开发过程中用的比较频繁,是基于 Promise 的 HTTP 库,那么接下来我们来好好理解它

我们知道 axios 的封装是基于 Promise 和 XMLHttpRequests,所以我们需要准备的前置知识有:

  • ajax 的封装
  • Promise
  • json-server

Ajax的封装

ajax的封装在面试的过程中也很有可能会问到,而且很多时候,我们需要了解请求的原理,那么 ajax 总共分为 4 步骤:

1、创建异步对象

let xhr = null
// 异步对象需要对 IE6 做兼容处理
if(window.XMLHttpRequest){    
    xhr = new XMLHttpRequest()
}else{    
// IE6 的异步对象    
    xhr = new ActiveXObject('Mricosoft.XMLHTTP')
}

2、链接服务器

get 和 post 的区别

    1、get 如果需要传递参数需要在 url 地址栏后面拼接,而 post 不需要

    2、post 需要加请求头,而 get 不需要

let url = '' // 请求的地址
let params = { // 发送给后端的参数
    name:'张三'
}

// get 方式
xhr.open('get', `${url}?name=${params.name}`)

// post 方式
xhr.open('post',url)
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded")

3、发送请求

get 和 post 的区别

  1. get 在 send 里面传递 null
  2. post 在 send 里面传递对象

let params = {
    name:'张三'
}
// get 方式
xhr.send(null)

// post 方式
xhr.send(params)

4、接收请求的数据

xhr.onreadystatechange = function(){
    // readyState:4 status:200 代表数据返回
    if(xhr.readyState === 4 && xhr.status === 200){
        // 拿到核心文本对象就是传递过来的数据
        console.log(xhr.responseText)
    }
}

Ajax封装完整代码

let options = {
    // 请求地址
    url:'',
    // 请求类型
    type:'get/post',
    // 传递的参数
    data:{ 
        name:'张三'
    },
    success:function(res){
        console.log(res)
    }
}
function ajax(options){
    let xhr = null
    // 1、创建异步对象
    if(window.XMLHttpRequest){
        xhr = new XMLHttpRequest()
    }else{
        xhr = new ActiveXObject('Mricosoft.XMLHTTP')
    }
    // 2、链接服务器
    if(options.type==='get'){
        xhr.open(options.type,`${options.url}?name=${options.data.name}`)
        xhr.send(null)
    }else{
        xhr.open(options.type,options.url)
        xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
        xhr.send(options.data)
    }
    // 3、接收请求数据
    xhr.onreadystatechange = function(){
        if(xhr.readyState ===4 && xhr.status === 200){
            options.success(xhr.responseText)
        }
    }
}

Promise

Promise我们从几个问题来解释:

  • Promise 的出现是为了解决什么问题 ?
  • Promise 在解决问题的过程中暴露了什么问题,generator 如何解决它的问题 ?
  • generator 暴露了什么问题,async 如何解决它的问题  ?

Promise 的出现为了解决什么问题 ?

Promise 的出现是为了解决回调地狱的问题的,但是它是如何解决回调地狱问题的呢,那么我们要知道 Promise 出现之前,我们用封装好的 ajax 去做异步请求,那么就存在一个问题,我们假设一个场景,我需求是请求三级联动的数据,省、市、区,分别三个接口,那我们会怎么操作,直接上代码,使用我们上面封装的 ajax

// 省、市、区
// 需求:通过选择省,再去发请求,选择城市,再发请求,请求到区
ajax({ // 全国有多少省
    url:'',
    type:'',
    data:data,
    success:function(res){
        ajax({ // 当前省有多少市
            url:'',
            type:'',
            data:data,
            success:function(res){
                ajax({ // 当前市有多少区
                    url:'',
                    type:'',
                    data:data,
                    success:function(res){
                        console.log(res) // 最终选择的区
                    }
                })
            }
        })
    }
})

注意:这样就形成了我们所说的回调地狱,不好维护,层层嵌套回调

Promise是如何解决回调地狱的问题呢?(这里我就用 setTimeout 去模拟数据请求了)

// 先请求省的数据 
new Promise(function(resolve,reject){
        setTimeout(function(){
            resolve('省')
        },1000)      
    }).then(res=>{
        console.log(res)
        // 再请求市的数据
        return new Promise(function(resolve,reject){
            setTimeout(function(){
                resolve('市')
            },1000)
        })
    }).then(res=>{
        console.log(res)
        // 再请求区的数据
        return new Promise(function(resolve,reject){
            setTimeout(function(){
                resolve('区')
            },1000)
        })
    }).then(res=>{
        console.log(res)
    })

注意:Promise,通过它独有的 then 的链式,将之前的三角回调地狱变成了,竖状结构,变得更好维护了。

问题:Promise 在解决回调地狱问题的时候也暴露了问题,就是需要返回一个 Promise 才能继续 then,那么接下来就是 generator 登场了

generator 的出现可以解决 Promise 带来的回调问题

怎么样去解决呢?

首先,什么是 generator 函数,它跟普通函数没有什么区别,只是在 function 后面,或者在函数名前面加 * 

// 下面两个都属于 generator 函数
function* gen(){}
function *gen(){}

其次,搭配 yield 使用

function *gen(){
    yield new Promise()
}

依然是省、市、区的例子

  function* gen() {    
        let res1 = yield Promise.resolve('省')    
        let res2 = yield Promise.resolve('市')    
        let res3 = yield Promise.resolve('区')  
    }  
// 需要用变量接收 generator 函数的执行  
let g = gen() 
// 需要使用 next 使用,当前只有省的数据,函数就停止执行console.log(g.next())    


我们得到的结果是一个对象,其中 done 代表整个 generator 还没有执行完,如果所有的 yield 执行完了,这个值就是 true ,value 是一个 Promise 对象通过 then 可以拿到结果


将函数执行完,更改代码

  function* gen() {    
        let res1 = yield Promise.resolve('省')    
        let res2 = yield Promise.resolve('市')    
        let res3 = yield Promise.resolve('区')  
    }  
    let g = gen()  
    g.next().value.then(res => {    
        console.log(res)  
    })  
    g.next().value.then(res => {    
        console.log(res)  
    })  
    g.next().value.then(res => {    
        console.log(res)  
    })

结果:


多说一句,其实 generator 涉及到了协程的概念,如果你懂协程,说明你还是不错的。

注意:generator 虽然解决了 Promise 的回调的问题,但是它本身的问题也比较明显,就是需要 next ,所以 async 就登场了

async 解决了 generator 的 next 的问题

async 大家应该都很熟悉了,就不做过多的阐述,搭配 await 来使用

async function asyncFn() {    
        let res1 = await Promise.resolve('省')    
        console.log(res1)    
        let res2 = await Promise.resolve('市')    
        console.log(res2)    
        let res3 = await Promise.resolve('区')    
        console.log(res3)  
}  
let async = asyncFn()

结果:


注意:很明显,async 不需要像 generator 那样 next , 直接一步到位,其实 async 就是 generator 的语法糖,async = generator + 自执行函数

json-server

这个工具可以直接像真实的请求后台数据一样,而且可以更改数据,并且很方便,后续都会用它来实现 axios 封装之后的调试

首先,安装

npm i -g json-server

创建一个文件夹 server,在此文件夹中新建一个文件 db.json

// db.json 的内容
{  "data": [
    {      
        "id": 1,      
        "name": "张三",      
        "age": "19"    
    },    
    {      
        "id": 2,      
        "name": "李四",      
        "age": "29"    
    }  
    ]
}

执行命令

json-server --watch --port 53000 db.json

这个时候会开启服务


通过 http://localhost:53000/data 可以拿到数据,并且也可以修改 db.json 里的数据

我们使用 axios 进行用例测试

新建一个 html 文件,引入 axios

<!DOCTYPE html>
    <html lang="en">
    <head>  
        <meta charset="UTF-8">  
        <meta name="viewport" content="width=device-width, initial-scale=1.0">  
        <title>Document</title>
    </head>
<body>
    <button onclick="getFn()">get请求</button>
    <button onclick="postFn()">post请求</button>
    <button onclick="putFn()">put请求</button></body>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.19.2/axios.js"></script>
<script>  
    function getFn() {    
        axios.get('http://localhost:53000/data/')
            .then(res => {      
                console.log(res)    
            })  
    } 
 
    function postFn() {    
        axios.post('http://localhost:53000/data/', 
            { 
                id: 3,      
                name: "王二麻子"    
            }).then(res => {      
                    console.log(res)    
            })  
    }  

    function putFn() {    
        axios.put('http://localhost:53000/data/2', 
            {      
                name: "赵四"    
            }).then(res => {      
                console.log(res)    
            })  }
</script>
</html>

页面出来的样式


我们分别点击 get post put 看看会发生什么事情

get请求


post请求


post 之后 db.json 数据的变化


向数据里面追加了一条数据

put请求


put 之后 db.json 中数据变化


数据第二条 name 变更为 “赵四”


所以这个就是 json-server 的基本使用,后续还会用到。如果感兴趣可以去查看其他的 API,json-server


最后:本人喜欢研究面试题,希望有更多志同道合的朋友一起来交流研究,可以帮助修改简历