AJAX
1 AJAX相关概述
1.1 AJAX简介
-
AJAX 全称为Asynchronous JavaScript And XML ,就是异步的 JS 和 XML 。
-
通过 AJAX 可以在浏览器中向服务器发送异步请求,最大的优势:无刷新获取数据。
-
AJAX 不是新的编程语言,而是一种将现有的标准组合在一起使用的新方式。
1.2 XML简介
-
XML 可扩展标记语言。
-
XML 被设计用来传输和存储数据。(而HTML是用来呈现数据)。
-
XML 和 HTML 类似,不同的是HTML中都是预定义标签,而XML中没有预定义标签,全都是自定义标签,用来表示一些数据。
//比如有一个学生数据:
//name="张三";age=22;gender="男";
//用XML表示:
<student>
<name>张三</name>
<age>22</age>
<gender>男</gender>
</student>
现在已经被JSON取代了。
//用JSON表示
{"name":"张三","age":22,"gender":"男"}
1.3 HTTP简介
-
HTTP (hypertext transport protocol)协议【超文本传输协议】,协议详细规定了浏览器和万维网服务器之间互相通信的规则。
-
请求报文:
一个HTTP请求报文由请求行、请求头部、空行和请求正文4部分组成。
例如:
请求行 GET /login HTTP/1.1
请求头 Host:www.baidu.com
Cookie:name=baidu
Content-type:application/x-www-form-urlencoded
User-Agent:chrome 83
空行
请求体 username=admin&password=123456
- 响应报文:
HTTP响应报文也由4部分组成,分别是:状态行、响应头部、空行、响应正文。
例如:
响应行 HTTP/1.1 200 OK
响应头 Content-type:text/html;charset=utf-8
Cookie-length:2048
Content-encoding:gzip
空行
响应体 <html>
<head>
</head>
<body>
<h1>尚硅谷</h1>
</body>
</html>
- 常见状态码
状态码 | 描述 | 简要说明 |
---|---|---|
200 | OK | 请求成功 |
201 | Created | 成功请求并创建了新的资源 |
401 | Unauthorized | 请求未经授权 |
403 | Forbidden | 服务器收到请求,但是拒绝提供服务。 |
404 | Not Found | 请求资源不存在 |
500 | Internal Server Error | 服务器内部错误 |
1.4 AJAX的特点
1.4.1 AJAX 的优点
-
可以无须刷新页面与服务器端进行通信。
-
允许你根据用户事件来更新部分页面内容。
1.4.2 AJAX 的缺点
- 没有浏览历史,不能回退
- 存在跨域问题(同源)
- SEO不友好(SEO指搜索引擎优化)(Ajax异步请求的数据不能被爬虫获取)
1.4.3 AJAX 请求与普通HTTP请求
- AJAX请求是一种特别的http请求。
- AJAX请求:通过xmlHttpRequest对象发送请求,无刷新获取数据。
- 普通HTTP请求:通过httpRequest对象发送请求,浏览器接收到服务器的响应后要刷新整个页面。
2 原生AJAX
2.1 前期准备
(1). 当前使用工具:vscode
(2). 安装 node.js
(3). 安装服务端框架 express
步骤:在vscode终端执行以下命令
1. 初始化环境:npm init --yes
2. 下载express包:npm install express --save
3. 编写js代码
// 1. 引入express
const express = require('express');
// 2. 创建应用对象
const app = express();
// 3. 创建路由规则
// request 是对请求报文的封装
// response 是对响应报文的封装
app.get('/', (request, response) => {
// 设置响应
response.send("HELLO EXPRESS");
});
// 4. 监听端口,启动服务
app.listen(8000, () => {
console.log("服务已经启动, 8000 端口监听中...");
})
(4). 运行js程序:node 文件名.js
(5).运行结果:
在浏览器中访问http://127.0.0.1:8000/
(6).可查看浏览器网络控制台中的请求与响应信息:
- 安装 nodemon(文件内容修改保存后自动重启服务)
步骤:
1.安装:npm install -g nodemon
2.启动服务:ndoemon server.js
2.2 AJAX的使用
2.2.1 核心对象
XMLHttpRequest,AJAX 的所有操作都是通过该对象进行的。
2.2.2 使用步骤
- 创建 XMLHttpRequest 对象
var xhr = new XMLHttpRequest();
- 设置请求信息
xhr.open(method, url);
//可以设置请求头,一般不设置
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
- 发送请求
xhr.send(body); // get 请求不传 body 参数,只有 post 请求使用
- 接收响应
// xhr.responseXML 接收 xml 格式的响应数据
// `在这里插入代码片`xhr.responseText 接收文本格式的响应数据
xhr.onreadystatechange = function (){
if(xhr.readyState == 4 && xhr.status == 200){
var text = xhr.responseText;
console.log(text);
}
}
2.2.3 案例
2.2.3.1 GET 请求
目标:点击发送请求返回响应信息并显示在下框中。
服务端: server.js
// 1. 引入express
const express = require('express');
// 解决跨域问题
const cors = require('cors');
// 2. 创建应用对象
const app = express();
// 解决跨域问题
app.use(cors());
// 3. 创建路由规则
// request 是对请求报文的封装
// response 是对响应报文的封装
app.get('/server', (request, response) => {
// 设置响应头,设置允许跨域
response.setHeader('Access-Control-Allow-Origin', '*');
// 设置响应体
response.send('HELLO AJAX GET');
});
// 4. 监听端口启动服务
app.listen(8000, () => {
console.log("服务已经启动,8000 端口监听中......");
});
启动服务:node server.js
或者 ndoemon server.js
前端页面: 1-GET.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">
<title>AJAX GET 请求</title>
<style>
#result {
width: 200px;
height: 100px;
border: 1px solid #90b;
}
</style>
</head>
<body>
<button>点击发送请求</button>
<div id="result"></div>
<script>
// 获取button元素
const btn = document.getElementsByTagName('button')[0];
const result = document.getElementById('result');
// 绑定事件
btn.onclick = function() {
// 1. 创建对象
const xhr = new XMLHttpRequest();
// 2. 初始化 设置请求方法和 URL
xhr.open('GET', 'http://127.0.0.1:8000/server?a=100&b=200&c=300');
// 发送
xhr.send();
// 4. 事件绑定 处理服务端返回的结果
// on when 当...时候
// readystate 是 xhr 对象中的属性,表示状态0 1 2 3 4
// 0:未初始化(初始值);1:表示open()方法调用完毕;2:表示send()方法调用完毕
// 3:表示服务端返回了部分的结果;4:表示服务端返回了所有的结果
// change 改变
xhr.onreadystatechange = function() {
// 判断 (服务端返回了所有的结果)
if (xhr.readyState === 4) {
// 判断响应状态码 200 404 403 401 500
// 2xx 成功
if (xhr.status >= 200 && xhr.status < 300) {
// 处理结果 行 头 空行 体
// 响应
// console.log(xhr.status); //状态码
// console.log(xhr.statusText); //状态字符串
// console.log(xhr.getAllResponseHeaders()); //所有响应头
// console.log(xhr.response); //响应体
// 设置 result 的文本
result.innerHTML = xhr.response;
} else {
}
}
};
};
</script>
</body>
</html>
结果:
2.2.3.2 POST 请求
server.js 中添加
// 可以接收任意类型的请求
app.all('/server', (request, response) => {
// 设置响应头,设置允许跨域
response.setHeader('Access-Control-Allow-Origin', '*');
// 设置响应头,设置所有信息都能接收到
response.setHeader('Access-Control-Allow-Header', '*');
// 设置响应体
response.send('HELLO AJAX POST');
});
2-POST.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">
<title>AJAX POST 请求</title>
<style>
#result {
width: 200px;
height: 100px;
border: 1px solid #903;
}
</style>
</head>
<body>
<div id="result"></div>
<script>
// 获取button元素
const result = document.getElementById('result');
// 绑定事件
result.addEventListener("mouseover", function() {
// 1. 创建对象
const xhr = new XMLHttpRequest();
// 2. 初始化 设置类型与URL
xhr.open('POST', 'http://127.0.0.1:8000/server');
// 设置请求头
xhr.setRequestHeader('Content-Type', "application/x-www-form-urlencoded");
// 3. 发送
xhr.send("a=100&b=200&c=300");
// xhr.send("a:100&b:200&c:300");
// xhr.send("123456789");
// 4. 事件绑定
xhr.onreadystatechange = function() {
//判断
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
// 处理服务端返回的结果
result.innerHTML = xhr.response;
}
}
}
});
</script>
</body>
</html>
2.2.3.3 JSON 数据
server.js 中添加:
// JSON响应
app.all('/json-server', (request, response) => {
// 设置响应头,设置允许跨域
response.setHeader('Access-Control-Allow-Origin', '*');
// 设置响应头,设置所有信息都能接收到
response.setHeader('Access-Control-Allow-Header', '*');
// 响应一个数据
const data = {
name: 'hello json'
};
// 对对象进行字符串转换
let str = JSON.stringify(data);
// 设置响应体
response.send(str);
});
3-JSON.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">
<title>AJAX JSON</title>
<style>
#result {
width: 200px;
height: 100px;
border: 1px solid #903;
}
</style>
</head>
<body>
<div id="result"></div>
<script>
// 获取button元素
const result = document.getElementById('result');
// 绑定事件
window.onkeydown = function() {
// 1. 创建对象
const xhr = new XMLHttpRequest();
// 设置响应体数据的类型
xhr.responseType = 'json';
// 2. 初始化 设置类型与URL
xhr.open('GET', 'http://127.0.0.1:8000/json-server');
// 3. 发送
xhr.send();
// 4. 事件绑定
xhr.onreadystatechange = function() {
//判断
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
// 处理服务端返回的结果
// result.innerHTML = xhr.response;
// 手动对数据转化
// let data = JSON.parse(xhr.response);
// console.log(data);
// result.innerHTML = data.name;
// 自动转换
result.innerHTML = xhr.response.name;
}
}
}
};
</script>
</body>
</html>
2.2.3.4 解决 IE 缓存问题
问题:在一些浏览器中(IE),由于缓存机制的存在,ajax 只会发送的第一次请求,剩余多次请求不会在发送给浏览器而是直接加载缓存中的数据。
解决方式:浏览器的缓存是根据 url 地址来记录的,所以我们只需要修改 url 地址即可避免缓存问题。
xhr.open("get","/testAJAX?t="+Date.now());
2.2.3.5 超时与网络异常
server.js 中添加
// 延时响应
app.get('/delay', (request, response) => {
// 设置响应头,设置允许跨域
response.setHeader('Access-Control-Allow-Origin', '*');
setTimeout(() => {
// 设置响应体
response.send('延时响应');
}, 3000);
});
5-超时与网络异常.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">
<title>请求超时与网络异常</title>
<style>
#result {
width: 200px;
height: 100px;
border: 1px solid #903;
}
</style>
</head>
<body>
<button>点击发送请求</button>
<div id="result"></div>
<script>
// 获取button按钮
const btn = document.getElementsByTagName("button")[0];
const result = document.getElementById("result");
// 绑定事件
btn.addEventListener("click", function() {
const xhr = new XMLHttpRequest();
// 超时设置 2s 设置
xhr.timeout = 2000;
// 超时回调
xhr.ontimeout = function() {
alert("网络异常,请稍后重试!!");
};
// 网络异常回调
xhr.onerror = function() {
alert("你的网络似乎出了一些问题!");
};
xhr.open('GET', 'http://127.0.0.1:8000/delay');
xhr.send();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status >= 200 & xhr.status < 300) {
result.innerHTML = xhr.response;
}
}
};
});
</script>
</body>
</html>
2.2.3.6 取消请求
6-取消请求.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">
<title>取消请求</title>
</head>
<body>
<button>点击发送</button>
<button>取消请求</button>
<script>
// 获取button按钮
const btns = document.querySelectorAll("button");
let xhr = null;
// 绑定事件
btns[0].onclick = function() {
xhr = new XMLHttpRequest();
xhr.open('GET', 'http://127.0.0.1:8000/delay');
xhr.send();
};
// abort
btns[1].onclick = function() {
xhr.abort();
};
</script>
</body>
</html>
2.2.3.7 请求重复问题
7-重复请求问题.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">
<title>请求重复问题</title>
</head>
<body>
<button>点击发送</button>
<script>
// 获取button按钮
const btns = document.querySelectorAll("button");
let xhr = null;
// 标识变量
let isSending = false; //是否正在发送AJAX请求
// 绑定事件
btns[0].onclick = function() {
// 判断标识变量
if (isSending) xhr.abort(); //如果正在发送,则取消该请求,创建一个新的请求
xhr = new XMLHttpRequest();
// 修改 标识变量的值
isSending = true;
xhr.open('GET', 'http://127.0.0.1:8000/delay');
xhr.send();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
// 修改标识变量
isSending = false;
}
};
};
</script>
</body>
</html>
2.2.4 AJAX 请求状态
xhr.readyState 可以用来查看请求当前的状态
0: 表示 XMLHttpRequest 实例已经生成,但是 open()方法还没有被调用。
1: 表示 send()方法还没有被调用,仍然可以使用 setRequestHeader(),设定 HTTP请求的头信息。
2: 表示 send()方法已经执行,并且头信息和状态码已经收到。
3: 表示正在接收服务器传来的 body 部分的数据。
4: 表示服务器数据已经完全接收,或者本次接收已经失败了
3 使用jQuery 发送 AJAX请求
3.1 get 请求
格式:
$.get(url, [data], [callback], [type])
url:请求的 URL 地址。
data:请求携带的参数。
callback:载入成功时回调函数。
type:设置返回内容格式,xml, html, script, json, text, _default。
3.2 post 请求
格式:
$.post(url, [data], [callback], [type])
url:请求的 URL 地址。
data:请求携带的参数。
callback:载入成功时回调函数。
type:设置返回内容格式,xml, html, script, json, text, _default。
3.3 通用方法
server.js 中添加
// jQuery 服务
app.all('/jquery-server', (request, response) => {
// 设置响应头,设置允许跨域
response.setHeader('Access-Control-Allow-Origin', '*');
// 设置响应头,设置所有信息都能接收到
response.setHeader('Access-Control-Allow-Header', '*');
const data = { name: "hello json" };
// 设置响应体
response.send(JSON.stringify(data));
});
jquery.html
$('button').eq(2).click(function() {
$.ajax({
// url
url: 'http://127.0.0.1:8000/jquery-server',
// 参数
data: {
a: 100,
b: 200
},
// 请求类型
type: 'GET',
// 响应体结果设置
dataType: 'json',
// 成功的回调
success: function(data) {
console.log(data);
},
// 超时事件
timeout: 5000,
// 失败的回调
error: function() {
console.log("出错了");
},
// 头信息
Headers: {
c: 300,
d: 400
},
});
});
4 使用axios发送AJAX请求
server.js 中添加
// axios 服务
app.all('/axios-server', (request, response) => {
// 设置响应头,设置允许跨域
response.setHeader('Access-Control-Allow-Origin', '*');
// 设置响应头,设置所有信息都能接收到
response.setHeader('Access-Control-Allow-Header', '*');
const data = { name: "hello axios" };
// 设置响应体
response.send(JSON.stringify(data));
});
axios.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">
<title>axios 发送 AJAX 请求</title>
<!-- axios -->
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
</head>
<body>
<button>GET</button>
<button>POST</button>
<button>AJAX</button>
<script>
// http://github.com/axios/axios
const btns = document.querySelectorAll('button');
// 配置 baseURL
axios.defaults.baseURL = 'http://127.0.0.1:8000';
btns[0].onclick = function() {
// GET 请求
axios.get('/axios-server', {
// url 参数
params: {
id: 100,
vip: 7
},
// 请求头信息
Headers: {
name: 'zhangsan',
age: 20
}
}).then(value => {
console.log(value); //响应结果
})
}
btns[1].onclick = function() {
// POST 请求
axios.post('/axios-server', {
username: 'admin',
password: 'admin'
}, {
params: {
id: 200,
vip: 9
},
Headers: {
height: 'zhangsan',
weight: '48'
}
});
}
btns[2].onclick = function() {
axios({
// 请求方法
method: 'POST',
// url
URL: '/axios-server',
// url 参数
params: {
vip: 10,
level: 30
},
// 头信息
Headers: {
a: 100,
b: 200
},
// 请求体参数
data: {
username: 'admin',
password: 'admin'
}
}).then(response => {
// 响应状态码
console.log(response.status);
// 响应状态字符串
console.log(response.statusText);
// 响应头信息
console.log(response.Headers);
// 响应体
console.log(response.data);
})
}
</script>
</body>
</html>
5 使用fetch发送AJAX请求
server.js 中添加
// fetch 服务
app.all('/fetch-server', (request, response) => {
// 设置响应头,设置允许跨域
response.setHeader('Access-Control-Allow-Origin', '*');
// 设置响应头,设置所有信息都能接收到
response.setHeader('Access-Control-Allow-Header', '*');
const data = { name: "hello axios" };
// 设置响应体
response.send(JSON.stringify(data));
});
fetch.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">
<title>fetch 发送 AJAX 请求</title>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
</head>
<body>
<button>AJAX请求</button>
<script>
const btn = document.querySelector('button');
btn.onclick = function() {
fetch('http://127.0.0.1:8000/fetch-server', {
// 请求方法
method: 'POST',
// 请求头
headers: {
name: 'zhangsan'
},
// 请求体
body: 'username=admin&password=admin'
}).then(response => {
// return response.text();
return response.json();
}).then(response => {
console.log(response);
})
}
</script>
</body>
</html>
6 跨域
6.1 同源策略
- 同源策略(Same-Origin Policy)最早由 Netscape 公司提出,是浏览器的一种安全策略。
- 同源: 协议、域名、端口号 必须完全相同。
- 违背同源策略就是跨域。
6.2 如何解决跨域
6.2.1 JSONP
(1) JSONP 是什么?
JSONP(JSON with Padding),是一个非官方的跨域解决方案,纯粹凭借程序员的聪明才智开发出来,只支持 get 请求。
(2) JSONP 怎么工作的?
在网页有一些标签天生具有跨域能力,比如:img link iframe script。
JSONP 就是利用 script 标签的跨域能力来发送请求的。
(3) JSONP 的使用
1.动态的创建一个 script 标签
var script = document.createElement("script");
2.设置 script 的 src,设置回调函数
script.src = "http://localhost:3000/testAJAX?callback=abc";
function abc(data) {
alert(data.name);
};
3.将 script 添加到 body 中
document.body.appendChild(script);
4.服务器中路由的处理
router.get("/testAJAX" , function (req , res) {
console.log("收到请求");
var callback = req.query.callback;
var obj = {
name:"孙悟空", age:18
}
res.send(callback+"("+JSON.stringify(obj)+")");
});
(4) jQuery 中的 JSONP
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button id="btn">按钮</button>
<ul id="list"></ul>
<script type="text/javascript" src="./jquery-1.12.3.js"></script>
<script type="text/javascript">
window.onload = function () {
var btn = document.getElementById('btn')
btn.onclick = function () {
$.getJSON("http://api.douban.com/v2/movie/in_theaters?callback=?",function
(data) {
console.log(data);
//获取所有的电影的条目
var subjects = data.subjects;
//遍历电影条目
for(var i=0 ; i<subjects.length ; i++){
$("#list").append("<li>"+
subjects[i].title+"<br />"+
"<img src=\""+subjects[i].images.large+"\" >"+
"</li>");
}
});
}
}
</script>
</body>
</html>
6.2.2 CORS
- CORS 是什么?
CORS(Cross-Origin Resource Sharing),跨域资源共享。CORS 是官方的跨域解决方案,它的特点是不需要在客户端做任何特殊的操作,完全在服务器中进行处理,支持get 和 post 请求。跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源 - CORS 怎么工作的?
CORS 是通过设置一个响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应以后就会对响应放行。 - CORS 的使用
主要是服务器端的设置:
router.get("/testAJAX" , function (req , res) {
//通过 res 来设置响应头,来允许跨域请求
//res.set("Access-Control-Allow-Origin","http://127.0.0.1:3000");
res.set("Access-Control-Allow-Origin","*");
res.send("testAJAX 返回的响应");
});