好吧,其实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.三种状态
-
待定(pending):初始状态,既没有被兑现,也没有被拒绝
-
已兑现(fulfilled):操作成功完成
-
已拒绝(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>