在浏览器中向服务器发送异步请求,最大的优势就是无刷新获取数据。AJAX不是新的编程语言,而是一种将现有的标准组合在一起使用的新方式。 XML可扩展标记语言,被设计用来传输和存储数据。XML和HTML类似,不同的是HTML中都是预定义标签,而XML中没有预定义标签,全都是自定义标签,用来表示一些数据。 Ajax优点 无需刷新页面而与服务器端进行通信;允许根据用户事件来更新部分页面内容。 Ajax缺点 没有浏览历史,不能回退;存在跨域问题(不可以从a.com向b.com发送请求);SEO(搜索引擎优化)不友好(网页中爬虫爬不到)。
1. HTTP
HTTP: 超文本传输协议,详细规定了浏览器和万维网服务器之间互相通信的规则。 请求报文:
请求行
请求类型、路径、HTTP协议版本
请求头
Host、Cookie、Content-type、User-Agent...
空行
请求体
GET请求的请求体为空,POST请求可以不为空
\begin{array}{c|} \text{请求行} & \text{请求类型、路径、HTTP协议版本} \\ \text{请求头} & \text{Host、Cookie、Content-type、User-Agent...} \\ \text{空行}\\ \text{请求体} & \text{GET请求的请求体为空,POST请求可以不为空} \\ \end{array}
请求行请求头空行请求体?请求类型、路径、HTTP协议版本Host、Cookie、Content-type、User-Agent...GET请求的请求体为空,POST请求可以不为空? 请求头的格式:参数名: 参数值 响应报文:
响应行
HTTP协议版本、响应状态码、响应状态字符串
响应头
Content-type、Content-length、Content-encoding...
空行
响应体
html代码
\begin{array}{c|} \text{响应行} & \text{HTTP协议版本、响应状态码、响应状态字符串} \\ \text{响应头} & \text{Content-type、Content-length、Content-encoding...} \\ \text{空行}\\ \text{响应体} & \text{html代码} \\ \end{array}
响应行响应头空行响应体?HTTP协议版本、响应状态码、响应状态字符串Content-type、Content-length、Content-encoding...html代码? 浏览器将响应体的内容提取出来,进行解析。 查看报文: F12
→
\rightarrow
→ 网络
→
\rightarrow
→ 刷新页面
→
\rightarrow
→ 显示了当前加载网页的过程中所有发送的请求
→
\rightarrow
→ 点击第一个请求,关注“标头”、“响应”、“负载”
2. express
在项目文件夹的最上层中打开集成终端,安装expresscnpm install express --save ;在要使用express的文件上打开集成终端,npm init --yes ;用nodejs启动node 文件名.js 。
const express = require('express');
const app = express();
app.get('/', (request, response)=>{
response.send("Hello, Express");
});
app.listen(8000, ()=>{
console.log("服务已启动,8000端口监听中... ...");
});
3. Ajax基本操作
服务器端 server.js
const express = require('express');
const app = express();
app.get('/server', (request, response)=>{
response.setHeader('Access-Control-Allow-Origin', '*');
response.send("Hello, Ajax Get");
});
app.post('/server', (request, response)=>{
response.setHeader('Access-Control-Allow-Origin', '*');
response.send("Hello, Ajax Post");
});
app.listen(8000, ()=>{
console.log("服务已启动,8000端口监听中... ...");
});
3.1. GET
<!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">
<title>Ajax Get Request Learn</title>
<style>
#result {
width: 200px;
height: 100px;
border: solid 1px #90b;
margin-top: 20px;
}
</style>
</head>
<body>
<button type="submit">send request</button>
<div id="result"></div>
<script>
const btn = document.getElementsByTagName("button")[0];
const result = document.getElementById("result");
btn.onclick = function(){
const xhr = new XMLHttpRequest();
xhr.open("GET", "http://127.0.0.1:8000/server");
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
result.innerHTML = xhr.response;
}
}
}
}
</script>
<script type="text/javascript" src="./server.js"></script>
</body>
</html>
url参数设置
- 在地址后面用问号隔开地址和参数;
- 参数名=参数值;
- 多个参数用 & 隔开;
- 举例:
http://127.0.0.1:8000/server?a=100&b=200
3.2. POST
<!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">
<title>Ajex Post Request Learn</title>
<style>
div{
width: 200px;
height: 100px;
border: solid 1px #90b;
margin-top: 20px;
}
</style>
</head>
<body>
<div id="result"></div>
<script>
const result = document.getElementById("result");
result.addEventListener("mouseover", function(){
const xhr = new XMLHttpRequest();
xhr.open('POST', 'http://127.0.0.1:8000/server');
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
result.innerHTML = xhr.response;
}
}
}
});
</script>
<script type="text/javascript" src="./server.js"></script>
</body>
</html>
设置参数:
- 请求体:向
send() 函数传参来设置 参数名=参数值(其实只要是服务器能处理的格式都可以); 多个参数用& 隔开; 举例:xhr.send(a=100&b=200); - 请求头:在
open 函数后面添加xhr.setRequestHeader(请求头, 请求值); 自定义请求头 02:35
3.3. 服务端响应 jsonJSON.parse()、JSON.stringify()
按下按键,显示响应体
<!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">
<title>Ajax Json Response</title>
<style>
div{
width: 200px;
height: 100px;
border: solid 1px #90b;
margin-top: 20px;
}
</style>
</head>
<body>
<div id="result"></div>
<script>
const result = document.getElementById("result");
window.onkeydown = function(){
const xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.open("GET","http://127.0.0.1:8000/json-server");
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
result.innerHTML = xhr.response.name;
}
}
};
};
</script>
</body>
</html>
const express = require('express');
const app = express();
app.all('/json-server', (request, response)=>{
response.setHeader('Access-Control-Allow-Origin', '*');
response.setHeader('Access-Control-Allow-Headers', '*');
const data = { name: 'yyt'};
let str = JSON.stringify(data);
response.send(str);
});
app.listen(8000, ()=>{
console.log("服务已启动,8000端口监听中... ...");
});
3.5. 请求超时与网络异常 timeout 、ontimeout 、onerror
<button type="submit">send request</button>
<div id="result"></div>
<script>
const result = document.getElementById("result");
const btn = document.getElementsByTagName("button")[0];
btn.addEventListener("click", function(){
const xhr = new XMLHttpRequest();
xhr.timeout = 2000;
xhr.ontimeout = function(){
alert('The network is abnormal. Try again later.');
};
xhr.onerror = function(){
alert('The network is abnormal.');
};
xhr.open('GET', 'http://127.0.0.1:8000/server');
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
result.innerHTML = xhr.response;
}
}
}
});
</script>
3.6. 取消请求 abort()
<body>
<button type="submit">send request</button>
<button type="submit">cancel request</button>
<div id="result"></div>
<script>
const result = document.getElementById('result');
const btns = document.querySelectorAll('button');
let xhr = null;
btns[0].onclick = function(){
xhr = new XMLHttpRequest();
xhr.open('GET', 'http://127.0.0.1:8000/server');
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
result.innerHTML = xhr.response;
}
}
}
}
btns[1].onclick = function(){
xhr.abort();
}
</script>
</body>
3.7. 请求重复发送问题
发送一个请求时检查之前有没有发送过同样的请求,如果有,那么取消之前的请求。
<body>
<button type="submit">send request</button>
<div id="result"></div>
<script>
const result = document.getElementById('result');
const btns = document.querySelectorAll('button');
let xhr = null;
let ifSending = false;
btns[0].onclick = function(){
if(ifSending){
xhr.abort();
};
xhr = new XMLHttpRequest();
ifSending = true;
xhr.timeout = 4000;
xhr.ontimeout = function(){
alert('The network is abnormal. Try again later.');
};
xhr.onerror = function(){
alert('The network is abnormal.');
};
xhr.open('GET', 'http://127.0.0.1:8000/server');
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
ifSending = false;
if(xhr.status >= 200 && xhr.status < 300){
result.innerHTML = xhr.response;
}
}
}
}
</script>
</body>
3.8. Axios发送Ajax请求
在BootCDN里搜索axios,找到引用路径
<script crossorigin="anonymous" src="https://cdn.bootcdn.net/ajax/libs/axios/0.27.2/axios.js"></script>
<body>
<button type="submit">GET</button>
<button type="submit">POST</button>
<button type="submit">AJAX</button>
<div id="result"></div>
<script>
const result = document.getElementById('result');
const btns = document.querySelectorAll('button');
axios.defaults.baseURL = 'http://127.0.0.1:8000';
btns[0].onclick = function(){
axios.get('/server',{
params:{
id:100,
idx:7
},
headers:{
name:'yyt',
age:16
}
}).then(value => {
result.innerHTML = value.data + ', get';
});
};
btns[1].onclick = function(){
axios.post('/server',
{
username:'kirlant',
password:'123456'
},{
params:{
id:200,
idx:9
},
headers:{
height:158,
weight:53
}
}).then(value => {
result.innerHTML = value.data + ', post';
});
};
btns[2].onclick = function(){
axios({
method:'post',
url:'/server',
params:{
id:300,
idx:5
},
headers:{
interesting:'animation'
},
data:{
username:'kirlant',
password:'123456'
}
}).then(response => {
result.innerHTML = response.data + ', axios';
});
}
</script>
</body>
3.9. fetch()发送Ajax请求
<body>
<button type="submit">Fetch</button>
<div id="result"></div>
<script>
const result = document.getElementById('result');
const btn = document.querySelector('button');
btn.onclick = function(){
fetch('http://127.0.0.1:8000/server', {
method:'POST',
headers:{
name:'yyt'
},
body:'username=kirlant&password=123456'
}).then(response => {
return response.text();
}).then(response => {
result.innerHTML = response;
});
};
</script>
</body>
3.10. 同源策略
同源策略是浏览器的一种安全策略,违背同源策略就是跨域。Ajax默认遵循同源策略。 同源: 协议、域名、端口号必须完全相同。 演示:在浏览器中输入地址http://127.0.0.1:9000/sameOrigin
<!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">
<title>Ajax Json Response</title>
<style>
div{
width: 200px;
height: 100px;
border: solid 1px #90b;
margin-top: 20px;
}
</style>
</head>
<body>
<button type="submit">get user data</button>
<div id="result"></div>
<script>
const result = document.getElementById('result');
const btn = document.querySelector('button');
btn.onclick = function(){
const xhr = new XMLHttpRequest();
xhr.open('GET', '/data');
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
result.innerHTML = xhr.response;
}
}
}
}
</script>
</body>
</html>
app.get('/sameOrigin',(request, response)=>{
response.sendFile(__dirname + '/ajax.html');
});
app.get('/data',(request, response)=>{
response.send('user data');
});
app.listen(9000, ()=>{
console.log("服务已启动,9000端口监听中... ...");
});
3.10. 跨域解决方案
- JSONP
一个非官方的跨域解决方案,只支持get 请求。 网页中有一些标签天生具有跨域能力,比如img 、link 、iframe 、script ,JSONP就是利用script标签的跨域能力来发送请求的。 跨域演示: 示例一 右键open in default browser 用本地方法打开html文件,script 标签中引用http协议路径的app.js 文件。 示例二 script标签可以发送跨域请求(终于好像明白了一点),只能响应js代码。 示例三 跨域服务 jsonp-server 的响应结果是一个函数调用(模板字符串,”handle({"name":"lyl"})“ ),其参数的实参是我们想返回的数据。函数必须提前声明,这样script接收到之后才能调用。
<!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">
<title>Jsonp</title>
<style>
#result{
width: 300px;
height: 100px;
border: 1px solid rgb(117, 30, 136);
padding-left: 5px;
padding-right: 5px;
}
</style>
</head>
<body>
<div id="result"></div>
<script>
function handle(data){
const result = document.getElementById("result");
result.innerHTML = data.name;
};
</script>
<script src="http://127.0.0.1:5500/Ajax/app.js"></script>
</body>
</html>
<script src="http://127.0.0.1:8000/jsonp-server"></script>
//server.js
// jsonp演示
app.all('/jsonp-server', (request, response)=>{
// 设置响应体
response.send("console.log('jsonp-server, all')");
});
// jsonp.css
#result{
width: 300px;
height: 100px;
border: 1px solid rgb(117, 30, 136);
padding-left: 5px;
padding-right: 5px;
}
<body>
<div id="result"></div>
<script>
function handle(data){
const result = document.getElementById("result");
result.innerHTML = data.name;
};
</script>
<script src="http://127.0.0.1:8000/jsonp-server"></script>
</body>
// server.js
app.all('/jsonp-server', (request, response)=>{
// response.send("console.log('jsonp-server, all')");
const data = {
name : 'lyl'
};
let str = JSON.stringify(data);
response.end(`handle(${str})`);
});
- 原生JSONP实现
输入用户名,然后丧失焦点,丧失焦点时向服务端发送请求,检测用户名是否存在。
<!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">
<title>Jsonp</title>
<style>
#result{
width: 300px;
height: 100px;
}
</style>
</head>
<body>
<div id="result">
用户名:
<input type="text" id="username" title="kirlant">
<p></p>
</div>
<script>
const input = document.querySelector('input');
const p = document.querySelector('p');
function handle(data){
input.style.border = "solid 1px #f00";
p.innerHTML = data.msg;
};
input.onblur = function(){
let username = this.value;
const script = document.createElement('script');
script.src = 'http://127.0.0.1:8000/check-username';
document.body.appendChild(script);
}
</script>
</body>
</html>
app.all('/check-username', (request, response)=>{
const data = {
exist: 1,
msg:'user name has exist, you cannot use it'
};
let str = JSON.stringify(data);
response.end(`handle(${str})`);
});
- CORS (Cross-Origin Resource Sharing)跨域资源共享
CORS是官方的跨域解决方案,特点是不需要在客户端做任何特殊操作,完全在服务器中进行处理,支持get和post请求。CORS标准新增了一组HTTP首部字段,允许服务器声明哪些源通过浏览器有权限访问哪些资源。调用方法response.setHeader() 。 CORS通过设置一个响应头来告诉浏览器该请求允许跨域,浏览器收到该响应后就会对该响应放行。
app.all('/server', (request, response)=>{
response.setHeader('Access-Control-Allow-Origin', '*');
response.send("server, all");
});
|