Ajax学习
Ajax------异步请求
优点:异步,不需要刷新获取数据,结合相关事件可以实现局部刷新
缺点:
- 没有浏览历史,不能回退(history)
- 存在跨域问题(浏览器同源问题)
- 局部刷新获得的资源数据是不能被爬虫获得(对SEO不友好)
Ajax是基于Http协议进行数据传输
浏览器与服务器之间数据交互详细过程见:https://xhx.huagecloud.top/archives/ji-suan-ji-wang-luo-------http-xie-yi
这里详细补充TCP三次握手建立连接后是如何进行用http请求进行数据交互的(一般情况就是一来一回)
请求报文(来服务器)
-
行 post /s?id=666 HTTP/1.1 方法 路径(url参数) 协议(版本)
-
头
Host: baidu.com 请求域名
Cookie: name=sikara
Content-Type: application/x-www-form-urlencoded 告知浏览器请求体参数的类型
User-Agent: chrome 83
-
空行
-
(请求)体 a=123&b=1235
响应报文(回浏览器)
-
行 HTTP/1.1 200 OK 协议版本 响应状态码 响应状态字符串
-
头
Content-Type: text/html;charset=utf-8 对响应体进行相关描述
content-encoding: gzip
content-length: 2048
-
空行
-
响应体
<html lang="en"> <head> <title>Document</title> </head> <body> <h1>sikara</h1> </body> </html>
## 实践代码
浏览器
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<title>Document</title>
</head>
<body>
<button id="get">get</button>
<button id="post">post</button>
<button id="pa">pa</button>
<button id="json">json</button>
<button id="delay">delay</button>
<button id="axios">axios</button>
<div></div>
</body>
<script>
let get = document.querySelector('#get');
let post = document.querySelector('#post');
let pa = document.querySelector('#pa');
let json = document.querySelector('#json');
let delay = document.querySelector('#delay');
let div = document.querySelector('div');
let axio = document.querySelector('#axios');
get.onclick = function(){
let xhr = new XMLHttpRequest();
// xhr.open('get','http://localhost:80/a')
xhr.open('get','http://localhost/a?asd=22&wa=35')
//get请求体默认undefined
xhr.send()
xhr.onreadystatechange = function(){
//服务端返回了所有的结果
if(xhr.readyState === 4){
//2xx都是成功
if(xhr.status >=200 && xhr.status <300){
//状态码
console.log(xhr.status);
//状态字符串
console.log(xhr.statusText);
//所有响应头
console.log(xhr.getAllResponseHeaders());
div.innerHTML = xhr.response
}
}
}
}
pa.onclick = function(){
let xhr = new XMLHttpRequest();
//添加时间戳,保证每次请求不同且最新
xhr.open('get','http://localhost/c?t='+Date.now())
//get请求体默认undefined
xhr.send('21/26')
xhr.onreadystatechange = function(){
//服务端返回了所有的结果
if(xhr.readyState === 4){
//2xx都是成功
if(xhr.status >=200 && xhr.status <300){
//状态码
console.log(xhr.status);
//状态字符串
console.log(xhr.statusText);
//所有响应头
console.log(xhr.getAllResponseHeaders());
div.innerHTML = xhr.response
}
}
}
}
post.onclick = function(){
let xhr = new XMLHttpRequest()
xhr.open('post','http://localhost/b')
//设置请求头 Content-Type设置请求体内容的类型 原生ajax不会帮你设置
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded')
xhr.send('a=1&b=2&c=3')//queryString写法
//json传参
// xhr.setRequestHeader('Content-Type','application/json')
// xhr.send(JSON.stringify({a:'sss'}))
// xhr.send('1','2')
xhr.onreadystatechange = function(){
//服务端返回了所有的结果
if(xhr.readyState === 4){
//2xx都是成功
if(xhr.status >=200 && xhr.status <300){
//状态码
console.log(xhr.status);
//状态字符串
console.log(xhr.statusText);
//所有响应头
console.log(xhr.getAllResponseHeaders());
div.innerHTML = xhr.response
}
}
}
}
json.onclick = function(){
let xhr = new XMLHttpRequest()
xhr.open('post','http://localhost/json')
//设置请求头 Content-Type设置请求体内容的类型 原生ajax不会帮你设置
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded')
// xhr.send(JSON.stringify({a:'sss'}))
xhr.send()
//提前自动处理 设置响应数据类型 这样返回的数据直接就是对象类型了
xhr.responseType = 'json'
xhr.onreadystatechange = function(){
//服务端返回了所有的结果
if(xhr.readyState === 4){
//2xx都是成功
if(xhr.status >=200 && xhr.status <300){
//状态码
console.log(xhr.status);
//状态字符串
console.log(xhr.statusText);
//所有响应头
console.log(xhr.getAllResponseHeaders());
console.log(typeof xhr.response);
//手动处理
// let {name} = JSON.parse(xhr.response)
//自动处理
let {name} = xhr.response
div.innerHTML = name
}
}
}
}
delay.onclick = (function(){
//阻止重复请求的设计思路是抖动 当请求正在处理时间内,不允许发送新请求
let xhr = new XMLHttpRequest() //缺点全局变量容易产生污染
let isSending = false //判断重复请求标准
//采用闭包方式
return function () {
//抖动方式阻止重复请求---在请求响应完成之前,isSending都会是true
// if (isSending) {
// console.log(isSending);
// xhr.abort()
// }
// isSending = true
//手动取消请求
// setTimeout(() => {
// alert('abort手动取消请求');
// xhr.abort()
// }, 1000);
//网络异常回调
xhr.onerror = function (params) {
alert('网络不稳定')
}
//节流设计思路
if(!isSending) {
xhr.open('get','http://localhost/delay')
xhr.send()
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
// isSending = false//请求有可能失败,反正只需要响应完毕就可以状态变更
isSending = false//请求有可能失败,反正只需要响应完毕就可以状态变更
if (xhr.status >= 200 && xhr.status < 300) {
console.log(xhr.response);
div.innerHTML = xhr.response
}
}
}
}
isSending = true
//超时2s设置 //属于自动取消请求
// xhr.timeout = 2000
//超时回调
xhr.ontimeout = function (params) {
alert('请求超时')
}
}
})()
//全局默认域名
axios.defaults.baseURL = 'http://localhost'
axio.onclick = function () {
axios({
method:'post',
url:'/axios',
headers:{//请求头不能带中文,自己写的请求头是会触发预检请求的
'name':123,
'sikara':666
},
params:{//url的参数
a:123
},
//请求体参数
data: {
firstName: 'Fred'
},
}).then(res=>{
console.log(res.readyState);
console.log(res.status);
console.log(res.statusText);
console.log(res.data);
console.log(res.headers);
})
}
</script>
</html>
服务端(需要用到cors的部分配置)
const express = require("express");
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.get("/a", (req, res) => {
res.setHeader("Access-Control-Allow-Origin", "*");
console.log(req.params, req.query, req.body);
res.send({ a: 5343 });
});
//可以接受任意类型请求,发送预检请求需要有路由接收处理
app.all("/b", (req, res) => {
res.setHeader("Access-Control-Allow-Origin", "*");
// res.setHeader("Access-Control-Allow-Methods", "*");
res.setHeader("Access-Control-Allow-Headers", "*");
console.log(req.params, req.query, req.body, req.body.a);
res.send("111");
});
//针对IE缓存问题:服务端数据更新但客户端数据不能对应更新
app.get("/c", (req, res) => {
res.setHeader("Access-Control-Allow-Origin", "*");
console.log(req.params, req.query, req.body);
res.send("cccc2222");
});
app.post("/json", (req, res) => {
res.setHeader("Access-Control-Allow-Origin", "*");
// res.setHeader("Access-Control-Allow-Methods", "*");
res.setHeader("Access-Control-Allow-Headers", "*");
console.log(req.params, req.query, req.body, req.body.a);
let data = {
name: "siakra",
};
res.send(data); //已经做了JSON数据默认转换
// res.send(JSON.stringify(data));
});
//延迟响应
app.get("/delay", (req, res) => {
res.setHeader("Access-Control-Allow-Origin", "*");
console.log(req.params, req.query, req.body);
setTimeout(() => {
res.send("delay");
}, 3000);
});
//axios
app.all("/axios", (req, res) => {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Headers", "*");
console.log(req.params, req.query, req.body);
res.send("axios");
});
app.listen(80, () => {
console.log("start");
});
补充说明
-
get请求的请求体默认是undefined,因此原生ajax携带参数只能写在url上
-
预检请求路径携带的参数也会跟着携带进服务器(请求体的数据则是正式请求再传输),但是会等正式请求再响应
-
原生ajax对于返回的json数据是需要预处理的,axio不需要
-
取消请求
- 自动取消:使用timeout属性,超过时限自动取消
- 手动取消:核心是调用abort方法------应用例子就是重复点击发送请求的次数处理(采用抖动,节流的设计思路就可实现)
-
回调函数: 超时回调ontimeout 网络异常回调onerror