XMLHttpRequest
大约 7 分钟
XMLHttpRequest
什么是 XMLHttpRequest 对象?
XMLHttpRequest(简称xhr)是浏览器提供的Javascript对象,通过它,可以***请求服务器上的数据资源***
使用 xhr 发起不带参数的 GET 请求
步骤:
- 创建 xhr 对象
- 调用 xhr.open()函数
- 调用 xhr.send()函数
- 监听 xhr.onreadystatechange 事件
// (1) 创建XHR对象
const xhr = new XMLHttpRequest();
// (2) 调用open()函数,指定请求方式与URL地址
xhr.open("GET", "http://www.liulongbin.top:3006/api/getbooks");
// (3) 调用send()函数,发起Ajax请求
xhr.send();
// (4) 监听 onreadystatechange事件
xhr.onreadystatechange = function () {
// 4.1 监听xhr对象的请求状态 readyState;与服务器的响应状态 status
if (xhr.readyState === 4 && xhr.status === 200) {
// 4.2 打印服务器响应回来的数据
console.log(xhr.responseText);
}
};
xhr 对象的 readyState 属性
XMLHttpRequest 对象的**readystate 属性,用来表示当前的 Ajax 请求所处的状态**。每个 Ajax 请求必然处于一下状态中的一个:
| 值 | 状态 | 描述 |
|---|---|---|
| 0 | UNSENT | XMLHttpRequet 对象已被创建,但尚未调用 open 方法 |
| 1 | OPEND | open 方法已经被调用 |
| 2 | HEADERS_RECEIVED | send 方法已经被调用,响应头也已经被接收 |
| 3 | LOADING | 数据接收中,此时 response 属性已经包含部分数据 |
| 4 | DONE | Ajax 请求完成,这意味着数据已经彻底的**完成或者失败** |
使用 xhr 发起带参数的 GET 请求
使用 xhr 对象发起带参数额 GET 请求时,只需要在调用 xhr.open 期间,为 URL 地址指定参数即可:
// ...省略不必要的代码
xhr.open("GET", "http://www.liulongbin.top:3006/api/getbooks?id=1");
// ...省略不必要的代码
这种在 URL 地址后面拼接的参数,叫做**查询字符串**。
什么是查询字符串
定义:查询字符串(URL 参数)是在 URL 的末尾加上用于向服务器发送信息的字符串(变量)。
格式:将英文的**?放在 URL 的末尾,然后再加上参数=值,想要加上多个参数的书啊,使用&**符号进行分割。以这个形式,可以将想要发送给服务器的内容添加到 URL 中。
示例:
//不带参数的URL地址
http: //www.liulongbin.top:3006/api/getbooks
//带一个参数的URL地址
http: //www.liulongbin.top:3006/api/getbooks?id=1
//带两个参数的URL地址
http: //www.liulongbin.top:3006/api/getbooks?id=1&bookname=西游记
GET 请求携带参数的本质
无论使用.get(),又或者直接使用 xhr 对象发起 GET 请求,当需要携带参数携带参数的时候,本质上都是直接将参数以查询字符串的形式,追加到 URL 地址的后面,发送到服务器的。
$.get(
"url",
{
name: "zs",
age: 20,
},
function () {}
);
//等价于
$get("url?name=zs&age=20", function () {});
$ajax({
method: "GET",
url: "url",
data: {
name: "zs",
age: 20,
},
success: function () {},
});
//等价于
$ajax({
method: "GET",
url: "url?name=zs&age=20",
success: function () {},
});
URL 编码和解码
URL 地址中,只允许出现英文相关的字母、标点符号、数字,因此,在 URL 地址中不允许出现中文字符。
如果 URL 中需要包含中文这样的字符,则必须对中文字符进行编码(转义)。
URL 编码的原则:使用安全的字符(没有特殊用途或者特殊意义的可打印字符)去表示那些不安全的字符。
URL 编码原则的通俗理解:使用英文字符去表示非英文字符。
http: //www.liulongbin.top:3006/api/getbooks?id=1&bookname=西游记
//经过URL编码之后,URL地址变成了如下格式:
http: //www.liulongbin.top:3006/api/getbooks?id=1&bookname=%E8%A5%BF%E6%B8%B8%E8%AE%B0
如何对 URL 进行编码与解码
浏览器提供了 URL 编码与解码的 API,分别是:
- encodeURI() 编码的函数
- decodeURI() 解码的函数
//(1) 使用encodeURI()编码
console.log(encodeURI("js高级程序设计")); // 输出结果:js%E9%AB%98%E7%BA%A7%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1
//(2) 使用decodeURI()解码
console.log(decodeURI("%E7%A8%8B%E5%BA%8F")); // 输出结果:程序
URL 编码的注意事项
由于浏览器会自动对 URL 地址进符编码 操作,因此,大多数情况下,程序员不需要关心 URL 地址的编码与解码操作。
使用 xhr 发起 POST 请求
// (1) 创建xhr对象
const xhr = new XMLHttpRequest();
// (2) 调用open()
xhr.open("POST", "http://www.liulongbin.top:3006/api/addbook");
// (3) 设置 Content-Type 属性(固定写法)
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
// (4) 调用 send() 同时将数据以查询字符串的形式,提交给服务器
xhr.send("bookname=你好啊&author=我很好&publisher=那就好");
// (5) 监听 onreadystatechange事件
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText);
}
};
XHR Level2 的新特性
可以设置 HTTP 请求的时限
const xhr = new XMLHttpRequest();
// 设置超时时间
xhr.timeout = 30;
// 设置超时以后的处理函数
xhr.ontimeout = function () {
// 如果请求时长超过30毫秒,就打印请求超时了。
console.log("请求超时了!");
};
xhr.open("GET", "http://www.liulongbin.top:3006/api/getbooks");
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText);
}
};
可以使用 FormData 对象管理表单数据
Ajx 操作往往用来提交表单数据。为了方便表单处理,HTML5 新增了一个 FormData 对象,可以模拟表单操作:
FormData 的基本使用
// 1.创建FormData实例
const fd = new FormData();
// 2.调用append函数,向fd中追加数据
fd.append("uname", "steven");
fd.append("upwd", "123");
// 3. 创建 XHR对象
const xhr = new XMLHttpRequest();
// 4. 指定请求类型与URL地址
xhr.open("POST", "http://www.liulongbin.top:3006/api/formdata");
// 5.直接提交 FormData对象 ,这与提交网页表单效果完全一样
xhr.send(fd);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(JSON.parse(xhr.responseText));
}
};
使用 FormData 快速获取表单中的值
<body>
<form id="form1">
<input type="text" name="uname" autocomplete="off">
<input type="password" name="upwd">
<button type="submit">提交</button>
</form>
<script>
const form = document.querySelector('#form1');
form.addEventListener('submit', function(e) {
// 阻止表单默认提交行为
e.preventDefault();
// 创建FormData,快速获取到form表单中的数据
const fd = new FormData(form);
const xhr = new XMLHttpRequest();
xhr.open('POST', 'http://www.liulongbin.top:3006/api/formdata');
xhr.send(fd);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(JSON.parse(xhr.responseText));
}
}
})
</script>
</body>
可以上传文件
<body>
<!-- 1.文件选择框 -->
<input type="file" id="file1"> <br>
<!-- 2. 上传文件的按钮 -->
<button id="btnUpload">上传文件</button>
<!-- 3. img标签,来显示上传成功以后的图片 -->
<img src="" alt="" id="img" width="400px">
<script>
// 1.获取上传文件的按钮
const btn = document.querySelector('#btnUpload');
// 2.为按钮绑定单机处理事件函数
btn.addEventListener('click', function() {
// 3.获取到用户选择的文件列表
const files = document.querySelector('#file1').files; // 因为files是一个数组,第一个文件就是files[0]
if (files.length <= 0) { // 如果没有选择文件,就不要执行后续的操作,提示用户
return alert('请选择要上传的文件!')
}
const fd = new FormData();
// 将用户选择的文件添加到formData中
fd.append('avater', files[0])
const xhr = new XMLHttpRequest();
xhr.open('POST', 'http://www.liulongbin.top:3006/api/upload/avatar');
xhr.send(fd);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
const data = JSON.parse(xhr.responseText);
if (data.status === 200) {
// 上传文件成功
// 将服务器返回的图片地址设置为<img>标签的src属性
document.querySelector('#img').src = 'http://www.liulongbin.top:3006' + data.url
}
} else {
// 上传文件失败
console.log(data.message);
}
}
})
</script>
</body>
可以获得数据传输的进度信息(上传文件并显示上传进度)
// 引入bootstrap.css文件
<body>
<div class="progress" style="width: 80%; margin: 20px auto 0;" id="js_bar">
<div class="progress-bar progress-bar-striped active" style="width: 0%;" id="percent">
</div>
</div>
<!-- 1.文件选择框 -->
<input type="file" id="file1"> <br>
<!-- 2. 上传文件的按钮 -->
<button id="btnUpload">上传文件</button>
<!-- 3. img标签,来显示上传成功以后的图片 -->
<img src="" alt="" id="img" width="400px">
<script>
// 1.获取上传文件的按钮
const btn = document.querySelector('#btnUpload');
const bar = document.querySelector('#js_bar')
// 2.为按钮绑定单机处理事件函数
btn.addEventListener('click', function() {
bar.style.display = 'block';
// 3.获取到用户选择的文件列表
const files = document.querySelector('#file1').files; // 因为files是一个数组,第一个文件就是files[0]
if (files.length <= 0) {
return alert('请选择要上传的文件!')
}
console.log('您选择了' + files.length + '个文件');
const fd = new FormData();
// 将用户选择的文件添加到formData中
fd.append('avater', files[0])
const xhr = new XMLHttpRequest();
// 监听文件上传的进度
xhr.upload.onprogress = function(e) {
// e.lengthComputable 是一个boolean值,表示当前上传的资源是否具有可计算的长度
if (e.lengthComputable) {
// e.loaded 已传输的字节
// e.total 需传输的总字节
// 计算出上传的进度
let procentComputable = Math.ceil((e.loaded / e.total) * 100);
const percent = document.querySelector('#percent');
percent.style.width = procentComputable + '%'; // 设置进度条宽度
percent.innerHTML = procentComputable + '%'; // 显示上传百分比
console.log('传输中' + procentComputable);
xhr.upload.onload = function() {
percent.className = 'progress-bar progress-bar-success' // 上传完成 进度条变绿色
setTimeout(function() {
bar.style.display = 'none'; // 隐藏进度条
procentComputable = 0; // 让百分比变为0
percent.innerHTML = ''; // 清空显示内容
percent.style.width = ''; // 清空进度条宽度
percent.className = 'progress-bar progress-bar-striped active'
}, 2000)
}
}
}
xhr.open('POST', 'http://www.liulongbin.top:3006/api/upload/avatar');
xhr.send(fd);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
const data = JSON.parse(xhr.responseText);
console.log(data);
if (data.status === 200) {
// 上传文件成功
// 将服务器返回的图片地址设置为<img>标签的src属性
document.querySelector('#img').src = 'http://www.liulongbin.top:3006' + data.url
}
} else {
// 上传文件失败
console.log(data.message);
}
}
})
</script>
</body>