axios背后的秘密

好吧,其实axios就是把XMLHttpRequest和Promise封装起来了 。 

一.XMLHttpRequest

1.XMLHttpRequest与axios的关系


 axios 是对 XHR 相关代码进行了封装,让我们只关心传递的接口参数,所以axios确实大大降低了使用的门槛,但当我们请求的次数寥寥无几的时候,原生的方式也是可以快速调用的。而XMLHttpRequest又是大名鼎鼎的AJAX的实现原理。

2.基础使用语法


a.普通形式 

const xhr = new XMLHttpRequest()
xhr.open('请求方法', '请求url网址')
xhr.addEventListener('loadend', () => {
  // 响应结果
  console.log(xhr.response)
})
xhr.send()

b.携带单个查询参数

<body>
    <ul class="city"></ul>
    <script>
        const city = document.querySelector('.city')
        const xhr = new XMLHttpRequest()
        xhr.open('GET', 'http://hmajax.itheima.net/api/city?pname=河南省')
        xhr.addEventListener('loadend', () => {
            console.log(xhr.response);
            const data = JSON.parse(xhr.response)
            for (let i = 0; i < data.list.length; i++) {
                let li = document.createElement('li')
                li.innerHTML = `${data.list[i]}`
                city.appendChild(li)
            }

        })
        xhr.send()
    </script>
</body>

注意:原生 XHR 需要自己在 url 后面携带查询参数字符串,没有 axios 帮助我们把 params 参数拼接到 url 字符串后面了  ,且该接口是用到了网络上的黑马的接口。

 c.携带多个查询参数

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>查询地区</title>
    <style>
        .total {
            width: 500px;
            margin: 0 auto;
        }
    </style>
</head>

<body>
    <div class="total">
        <form action="">
            <div class="item1">
                <label for="">省份</label>
                <input type="text" class="province">
            </div>
            <div class="item2">
                <label for="">城市</label>
                <input type="text" class="city">
            </div>
        </form>
        <button class="btn">提交</button>
        <p>地区列表: </p>
        <ul class="list-group">
            <!-- 示例地区 -->
            <li class="list-group-item">东城区</li>
        </ul>
    </div>

    <script>
        const btn = document.querySelector('.btn')
        btn.addEventListener('click', () => {
            const pname = document.querySelector('.province').value
            const cname = document.querySelector('.city').value
            const searchObj = {
                pname,
                cname
            }
            const paramsObj = new URLSearchParams(searchObj)
            const params = paramsObj.toString()
            const xhr = new XMLHttpRequest()
            xhr.open('GET', `http://hmajax.itheima.net/api/area?${params}`)
            xhr.addEventListener('loadend', () => {
                //xhr.response为json字符串形式,将其转化为对象便于取到里面的数据
                const data = JSON.parse(xhr.response)
                const str = data.list.map(item => {
                    return `<li class="list-group-item">${item}</li>`
                }).join('')
                document.querySelector('.list-group').innerHTML = str
            })
            xhr.send()
        })
    </script>
</body>
</html>

        多个查询参数,如果自己拼接很麻烦,这里用 URLSearchParams 把参数对象转成“参数名=值&参数名=值“格式的字符串,语法如下:  

// 1. 创建 URLSearchParams 对象
const paramsObj = new URLSearchParams({
  参数名1: 值1,
  参数名2: 值2
})

// 2. 生成指定格式查询参数字符串
const queryString = paramsObj.toString()
// 结果:参数名1=值1&参数名2=值2

d.数据提交

  

      // 设置请求头-告诉服务器内容类型(JSON字符串),当然这并不是唯一,根据服务器能接收的来修改
      xhr.setRequestHeader('Content-Type', 'application/json')

 把要传的对象或其他的什么对象转换成对应的形式,再将***写入xhr.send()中,用来提交

二.Promise

1.回调地狱

        由深层嵌套的回调函数(即回调地狱)导致代码难以理解和维护的问题,promise的出现能比较优雅地解决这个问题

 2.Promise是什么 


  • Promise 对象用于表示一个异步操作的最终完成(或失败)及其结构值,目的就是解决回调地狱问题

 3.基本语法


// 1. 创建 Promise 对象
const p = new Promise((resolve, reject) => {
 // 2. 执行异步任务-并传递结果
 // 成功调用: resolve(值) 触发 then() 执行
 // 失败调用: reject(值) 触发 catch() 执行
})
// 3. 接收结果
p.then(result => {
 // 成功
}).catch(error => {
 // 失败
})

4.三种状态


  1. 待定(pending):初始状态,既没有被兑现,也没有被拒绝

  2. 已兑现(fulfilled):操作成功完成

  3. 已拒绝(rejected):操作失败

 

5.Promise.resolve

        使用场景通常是将缓存的数据转化为Promise对象,避免二次请求 

// 定义一个名为 ajax 的函数,它接受一个 URL 作为参数。
const ajax = function (url) {
    // 初始化一个静态属性 cache,如果它已经存在则使用现有的,否则创建一个新的对象。
    // 这个 cache 对象用于缓存数据,避免重复请求。
    var cache = ajax.cache || (ajax.cache = { data: null })

    // 检查 cache 中是否已经缓存了数据。
    if (cache.data) {
        console.log('走缓存');
        // 如果缓存中有数据,则直接返回这些数据而不是发送新的请求。
        return Promise.resolve(cache.data)
    }

    // 如果缓存中没有数据,返回一个新的 Promise 对象。
    return new Promise((resolve, reject) => {
        // 创建一个 XMLHttpRequest 对象来发送请求。
        const xhr = new XMLHttpRequest();
        // 设置请求方法为 GET,并打开与提供的 URL 的连接。
        xhr.open("get", url, true)
        // 发送请求。
        xhr.send()
        // 定义 onreadystatechange 事件的回调函数。
        xhr.onreadystatechange = function () {
            // 当请求完成时(readyState 等于 4)。
            if (xhr.readyState == 4) {
                // 如果 HTTP 状态码表示成功(在 200 到 300 之间)。
                if (xhr.status >= 200 && xhr.status <= 300) {
                    // 解析响应文本为 JSON,然后完成(resolve)Promise。
                    resolve(JSON.parse(xhr.response))
                    // 将解析后的响应数据缓存到 ajax.cache 中。
                    ajax.cache.data = JSON.parse(xhr.response)
                } else {
                    // 如果状态码表示失败,则拒绝(reject)Promise。
                    reject(xhr.responseText)
                }
            }
        }
    })
}

// 调用 ajax 函数请求 './1.json' 文件。
ajax('./1.json').then(res => {
    // 请求成功时,打印响应结果。
    console.log(res);
})

// 设置一个定时器,1秒后再次调用 ajax 函数。
setTimeout(() => {
    // 再次请求 './1.json' 文件,由于数据已经被缓存,所以这次将不会发送请求,而是直接使用缓存数据。
    ajax('./1.json').then(res => {
        // 打印响应结果,这将来自于缓存。
        console.log(res);
    })
}, 1000)

 

6.携带单个查询参数的Promise+XHR版

<body>
    <ul class="city"></ul>
    <script>
        const city = document.querySelector('.city')

        const p = new Promise((resolve, reject) => {
            const xhr = new XMLHttpRequest()
            xhr.open('GET', 'http://hmajax.itheima.net/api/city?pname=河南省')
            xhr.addEventListener('loadend', () => {
                // 2xx开头的都是成功响应状态码
                if (xhr.status >= 200 && xhr.status < 300) {
                    resolve(JSON.parse(xhr.response))
                } else {
                    reject(new Error(xhr.response))
                }
            })
            xhr.send()
        })

        p.then(result => {
            for (let i = 0; i < result.list.length; i++) {
                let li = document.createElement('li')
                li.innerHTML = `${result.list[i]}`
                city.appendChild(li)
            }
        }).catch(error => {
            city.innerHTML = error.message
        })
    </script>
</body>

 

三.封装简易的axios函数 


示例:与上述的携带多个参数的实例的效果相同,可以对比参照一下,因为实现的方式并不相同,

这其实就是一份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>
    <style>
        .total {
            width: 500px;
            margin: 0 auto;
        }
    </style>
</head>

<body>
    <div class="total">
        <form action="">
            <div class="item1">
                <label for="">省份</label>
                <input type="text" class="province">
            </div>
            <div class="item2">
                <label for="">城市</label>
                <input type="text" class="city">
            </div>
        </form>
        <button class="btn">提交</button>
        <p>地区列表: </p>
        <ul class="list-group">
            <!-- 示例地区 -->
            <li class="list-group-item">东城区</li>
        </ul>
    </div>

    <script>
        const btn = document.querySelector('.btn')
        function myAxios(config) {
            return new Promise((resolve, reject) => {
                const xhr = new XMLHttpRequest()
                if (config.params) {
                    const paramsObj = new URLSearchParams(config.params)
                    const paramsStr = paramsObj.toString()
                    config.url += `?${paramsStr}`
                }
                xhr.open(config.method || 'GET', config.url)
                xhr.addEventListener('loadend', () => {
                    if (xhr.status >= 200 && xhr.status < 300) {
                        resolve(JSON.parse(xhr.response))
                    } else {
                        reject(new Error(xhr.response))
                    }
                })
                if (config.data) {
                    // 2. 转换数据类型,在send中发送
                    const jsonStr = JSON.stringify(config.data)
                    xhr.setRequestHeader('Content-Type', 'application/json')
                    xhr.send(jsonStr)
                    } else {
                    // 如果没有请求体数据,正常的发起请求
                    xhr.send()
                }
            })
        }

        btn.addEventListener('click', () => {
            myAxios({
                url: 'http://hmajax.itheima.net/api/area',
                params: {
                    pname: document.querySelector('.province').value,
                    cname: document.querySelector('.city').value,
                }
            }).then(result => {
                const str = result.list.map(item => {
                    return `<li class="list-group-item">${item}</li>`
                }).join('')
                document.querySelector('.list-group').innerHTML = str
            }).catch(error => {
                document.querySelector('.list-group').innerHTML = error.message
            })
        })

    </script>
</body>

</html>

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值