文章目录
前言
Ajax即Asynchronous Javascript And XML(异步JavaScript和XML),是一种Web数据交互方式,它用来描述一种使用现有技术集合的‘新’方法,包括: HTML 或 XHTML, CSS, JavaScript, DOM, XML, XSLT, 以及重要的XMLHttpRequest,使用Ajax技术的网页应用能够快速地将增量更新呈现在用户界面上,而不需要重载(刷新)整个页面,即无刷新获取数据,这使得程序能够更快地回应用户的操作
XML - 可扩展标记语言
XML用来传输和存储数据,它和HTML类似,但HTML都是预定义标签,而XML中没有预定义标签,都是自定义标签,用来表示一些数据。现在被JSON取代
AJAX优点
- 可无需刷新页面而与服务器端进行通信
- 允许开发者根据用户事件来更新部分页面内容
AJAX缺点
- 无浏览历史,不可回退
- 存在跨域问题
- SEQ(搜索引擎优化)不友好
写在前面——AJAX常用API
XMLHttpRequest()
: 创建 XHR 对象的构造函数status
: 响应状态码值statusText
: 响应状态文本: 比如 “Not Found”readyState
: 标识请求状态的只读属性: 0、1、2、3、4onreadystatechange
: 绑定 readyState 改变的监听responseType
: 指定设置响应数据类型,如果设置为 ‘json’,得到响应后自动解析JSON数据并响应response
: 响应体数据,类型取决于 responseType 的指定timeout
: 设置请求超时时间,默认为0.代表无超时时间限制ontimeout
: 绑定超时监听onerror
: 绑定网络请求错误的监听open()
: 初始化一个请求,参数形式为:(method, url)
send(data)
: 发送请求,请求类型为GET
时不传参abort()
: 中断请求(此处中断的过程区间是:一个请求从发出到返回这段时间)getResponseHeader(name)
: 获取指定名称的响应头getAllResponseHeaders()
: 获取所有响应头组成的字符串setRequestHeader(name, value)
: 设置请求头的名称和值
1、HTTP协议请求报文与相应文本结构
HTTP协议
:超文本传输协议,详细规定了浏览器和万维网服务器之间互相通信的规则、约定
1.1、HTTP请求交互的基本过程**
- 前端应用从浏览器向服务端发送HTTP请求(请求报文)
- 后台服务器接收到请求后,调度服务器应用处理请求,并向浏览器端返回HTTP响应报文
- 浏览器端接收到响应,解析显示响应体或调用监视回调
1.2、请求报文
1、请求行
- 请求类型: GET、POST……
- url路径: /s?ie=utf-8
- HTTP协议版本
2、请求头
格式:名字: 值
(注意空格),以尚硅谷官网为例
HOST
: atguigu.comCookie
:name=guiguContent-type
:application/x-www-form-urlencodedUser-Agent
:chrome 83- ……
3、空行\n:回车
4、请求体
如果是GET请求,请求体就是空的;如果是POST,请求体可以不为空
user=jack&passwords=123
{"username":"jack","passwords":123}
1.2.1、不同类型的请求及作用 - 分别对应增、删、查、改
POST
: 向服务端添加新数据(增)DELETE
: 删除服务端数据(删)GET
: 从服务端读取数据(查)PUT
: 更新服务端已有的数据(改)
1.3、响应报文
1、 响应行
协议版本 响应状态码 响应状态字符串
比如: HTTP/1.1 200 OK
2、 响应头
Content-Type
: text/html;charset=utf-8Content-length
:2048Content-encoding
:gzip- ……
3、空行\n:回车
4、 响应体:html文本
1.3.1、常见的响应状态码
200 - OK
: 请求成功,一般用于GET与POST请求201 - Created
: 已创建,成功请求并创建了新的资源401 - Unauthorized
: 未(授权 / 请求 / 要求)用户的身份认证404 - Not Found
: 服务器无法根据客户端的请求找到资源500 - Internal Server Error
: 服务器内部错误,无法完成请求
1.4、浏览器控制台查看通信报文 - 以谷歌浏览器为例
打开网页 —> F12 —> 点击Network —> F5刷新
Headers 和 Response是重点
上图中,
Response Headers是响应头
,Request Headers是请求头
- 请求头(点击View parsed 可以看到原始报文)
Payload 载荷
中可以看到对URL参数的解析,很方便调试参数
-
响应头(点击View parsed可以看到原始报文)
–响应体在Response
里
-
Preview 预览
演示
先部署好Express框架
//引入express
const express = require("express");
//创建应用对象
const app = express();
//创建路由规则
app.get("/", (request, response)=>{
//设置响应,使用response.send()方法
response.send('hello');
})
//监听端口启动服务
app.listen(8000, ()=>{//两个参数:端口,执行方法
console.log('服务已启动,8000端口监听中');
})
在终端打开,并输入命令:node 脚本文件名,打开浏览器,在地址栏输入端口127.0.0.1:8000(设置的监听端口是8000)并跳转
F12 —> Network —> F5刷新,name选择127.0.0.1,在控制台可以看到整个交互
- 请求报文
- 完整的响应报文(两部分,端口给出的响应+响应体)
借此就可以与AJAX进行交互
1.5、AJAX请求与HTTP请求的区别
- 二者区别在浏览器端
- 只有
XMLHttpRequest
(及XHR
)或者fetch
发出的请求才是AJAX请求,其他都是非AJAX请求 - 浏览器端收到不同请求做出响应的区别
– AJAX请求:浏览器不对界面进行任何更新操作,只调用监视的回调函数
并传入相关响应数据
– 一般请求:浏览器刷新或跳转页面
,直接显示响应体数据
2、AJAX请求的核心操作
创建XMLHttpRequest对象
const xhr = new XMLHttpRequest();
使用
XMLHttpRequest
对象可以实现与服务器的交互(发送AJAX请求),同时,AJAX的所有操作也都是通过该对象进行
设置请求的方法和URL并发送
//初始化,设置请求的方法和url
xhr.open('GET','http://127.0.0.1:8000/server');
//open()方法传入两个参数,第一个是方式,第二个是URL
//发送
xhr.send();//GET请求不传参,POST请求可以传
接收响应,处理服务端返回的结果
xhr.onreadystatechange = function() {
//判断(服务端返回了所有结果)
if(xhr.readyState === 4){
//判断 响应状态码,比如200 404 403 401 500
//假设200+视为判定成功
if(xhr.status >= 200 && xhr.status < 300){
//处理结果,遵循行、头、空行、体
//1、状态码部分
console.log(xhr.status);//状态码
console.log(xhr.statusText);//状态字符串
console.log(xhr.getAllResponseHeaders());//所有响应头
console.log(xhr.response);//响应体
//设置result的文本
res.innerHTML = xhr.response;
/* xhr.responseXML 接收XML格式的响应数据
xhr.responseText接收文本格式的响应数据 */
}
}
}
完整案例
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>Document</title>
<script src="./DOM.js"></script>
<style>
#res{
width: 300px;
height: 130px;
border: solid 2px rgb(121, 139, 230);
}
</style>
</head>
<body>
<button>点击发送请求</button>
<div id="res"></div>
</body>
</html>
前端js
window.onload = function() {
//获取元素
const btn = document.getElementsByTagName("button")[0];
//绑定事件
btn.onclick = function() {
//创建对象
const xhr = new XMLHttpRequest();
//初始化,设置请求的方法和url
xhr.open('GET','http://127.0.0.1:8000/server');
//发送
xhr.send();
//事件绑定,处理服务端返回的结果
//on...when 当...时
//readystate 是xhr对象中的属性,表示状态 0 1 2 3 4
/* 0 - 未初始化,readystate最初的属性值
1 - open()方法已经调用完毕
2 - send()方法调用完毕
3 - 服务端返回了部分的结果
4 - 服务端返回了所有结果 */
xhr.onreadystatechange = function() {
//判断(服务端返回了所有结果)
if(xhr.readyState === 4){
//判断 响应状态码,比如200 404 403 401 500
//假设200+视为判定成功
if(xhr.status >= 200 && xhr.status < 300){
//处理结果,遵循行、头、空行、体
//1、状态码部分
console.log(xhr.status);//状态码
console.log(xhr.statusText);//状态字符串
console.log(xhr.getAllResponseHeaders());//所有响应头
console.log(xhr.response);//响应体
//设置result的文本
res.innerHTML = xhr.response;
}
}
}
}
}
服务端server.js
//引入express
const express = require("express");
//创建应用对象
const app = express();
//创建路由规则
app.get('/server', (request, response)=>{//第一个参数是url路径
//设置响应头,并设置允许跨域,注意这里参数不要输错
response.setHeader('Access-Control-Allow-Origin', '*');
//设置响应体
response.send('hello ajax');
});
//监听端口启动服务
app.listen(8000, ()=>{//两个参数:端口,执行方法
console.log('服务已启动,8000端口监听中');
})
3、 AJAX设置URL请求参数
在原先的URL后用?
分隔,后面传参,参数之间使用&
分隔
xhr.open('GET','http://127.0.0.1:8000/server?a=20&b=50&c=900');
在浏览器控制台里,可以在Query String Parameters
查看参数,能看到参数则说明参数发送成功
4、AJAX发送POST请求
server.js中添加POST
app.post('/server', (request, response)=>{//第一个参数是url路径
//设置响应头,并设置允许跨域,注意这里参数不要输错
response.setHeader('Access-Control-Allow-Origin', '*');
//设置响应体
response.send('hello ajax 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>Document</title>
<style>
#res{
width: 300px;
height: 130px;
border: solid 2px rgb(121, 139, 230);
}
</style>
</head>
<body>
<div id="res"></div>
<script>
const res = document.getElementById("res");
res.addEventListener('click', 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){
res.innerHTML = xhr.response;
}
}
}
})
</script>
</body>
</html>
5、POST请求设置请求体
在send()
方法里设置,参数形式参照URL的参数形式
xhr.send('str&a=500&c:600');
//可以设置服务端能处理的任意类型、任意格式的参数
6、AJAX设置请求头信息
一般来说,身份校验的信息会放在头信息里
格式:
xhr.setRequestHeader('请求体内部类型', '参数查询字符串类型');
实例:
//预定义请求头信息
xhr.setRequestHeader('Content-Type', 'application/x-www-from-urlencoded');
//也可以自定义头信息
xhr.setRequestHeader('name','ykyk');
自定义请求头信息
浏览器一般有安全机制,当设置了自定义请求头信息时,浏览器会报错,所以需要在后端手动添加一个特殊的响应头,并且把路由规则中的 post() 改为all()
,以便响应头能接受任意的请求头信息
增加自定义请求头
xhr.setRequestHeader('name','ykyk')
修改路由规则的方法为all(),以便能够接收任意请求头信息
//创建路由规则
app.all('/server', (request, response)=>{//第一个参数是url路径
//设置响应头,并设置允许跨域,注意这里参数不要输错
response.setHeader('Access-Control-Allow-Origin', '*');
//添加处理自定义请求头的特殊响应头,*表示所有类型头信息都可以接受
response.setHeader('Access-Control-Allow-Headers','*');
//设置响应体
response.send('hello ajax POST');
});
7、服务端响应JSON数据
<div id="res"></div>
前端的js:
const res = document.getElementById('res');
window.onkeydown = function() {
//发送请求
const xhr = new XMLHttpRequest();
//(JSON数据自动转换使用)提前设置响应体数据的类型
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){
console.log(xhr.response);
res.innerHTML = xhr.response;
/* 数据转化的其他方式(将JSON数据格式转化为JS对象的格式)
1、手动转化:let data = JSON.parse(xhr.response);
2、自动转化(需要提前设置响应体数据类型):
res.innerHTML = xhr.response.name; */
}
}
}
}
json-server.js
//引入express
const express = require("express");
//创建应用对象
const app = express();
//创建路由规则
app.all('/json-server', (request, response)=>{//第一个参数是url路径
//设置响应头,并设置允许跨域,注意这里参数不要输错
response.setHeader('Access-Control-Allow-Origin', '*');
//添加处理自定义请求头的特殊响应头,*表示所有类型头信息都可以接受
response.setHeader('Access-Control-Allow-Headers','*');
//响应一个数据
const data = {
name: 'jack'
};
let str = JSON.stringify(data);//序列化为JSON格式字符串
//设置响应体
response.send(str);//传给前端
});
//监听端口启动服务
app.listen(8000, ()=>{//两个参数:端口,执行方法
console.log('服务已启动,8000端口监听中');
});
进行数据转换后得到JS数据,可以直接使用
nodemon
自动重启工具安装:nodemon可以
监控我们的保存操作
,当我们修改文件之后进行保存那么文件将会进行自动更新
,可以很方便地辅助项目开发
- 首先要安装好node.js
- 然后vscode在终端运行文件,输入
npm install -g nodemon
,运行- 安装成功后,输入命令
npx nodemon 服务端文件名.js
,运行,这里前面用npx是因为不加的话vscode会显示系统禁止运行脚本
8、IE缓存问题解决
IE浏览器由于缓存机制,AJAX只会发送第一次请求,剩余多次请求不会再发送给浏览器而是直接加载缓存中的数据
解决方式:按照以下方式修改url,加上时间戳
//修改前
xhr.open('GET','http://127.0.0.1:8000/ie');
//添加时间戳
xhr.open('GET','http://127.0.0.1:8000/ie?t=' + Date.now());
9、AJAX请求超时控制与网络异常处理
- 超时控制
timeout
const xhr = new XMLHttpRequest();
//设置超时时间
xhr.timeout = 2000;
//超时回调函数
xhr.timeout = function() {
alert('网络异常, 请稍后重试!');
}
- 网络异常回调
onerror
xhr.onerror = function() {
alert('网络似乎出了一些问题, 清检查网络后重试');
}
10、AJAX取消请求
在请求成功但结果没有返回来之前,可以通过代码手动把请求取消
<button>发送请求</button>
<button>取消请求</button>
<script>
const btns = document.querySelectorAll('button');
let x = null;
btns[0].onclick = function() {
x = new XMLHttpRequest();
x.open("GET",'http://127.0.0.1:8000/delay');
x.send();
}
btns[1].onclick = function() {
//使用abort()方法取消请求
x.abort();
}
</script>
下面演示了在延时响应的情况下,请求结果返回后手动取消请求与请求结果返回前手动取消请求,方便理解abort()的作用
11、AJAX请求重复发送
在用户多次重复点击某一模块,在短时间内重复发送多个相同请求时,服务器会面临较大压力,这时需要采取方式缓解服务器压力
处理策略
1、取消之前请求,发送新的请求
使用标识变量记录是否有请求正在发送
//使用表示变量
let isSending = false;//是否正在发送AJAX请求
在新请求发送前进行判断,是否有正在发送的请求,如果有,俺么终止,让新请求发送
if (isSending) {//如果发送新请求时有请求正在发送,那么取消上一个请求,发送当前新请求
x.abort();
}
//创建新请求。并修改标识变量的值
x = new XMLHttpRequest();
isSending = true
x.open("GET",'http://127.0.0.1:8000/delay');
x.send();
请求发送完毕后,修改标识变量值
x.onreadystatechange = function() {
if (x.readyState === 4) {
isSending = false;
}
}
12、Axios发送AJAX请求
首先引入axios的js文件
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
然后用axios发送AJAX请求,只是举个例子
const btns = document.querySelectorAll('button');//总共获取了3个button
- axios发送GET请求
//GET请求
btns[0].onclick = function() {
axios.get('http://127.0.0.1:8000/axios',{
//axios传入url参数
p:{
id: 16,
time: 18
},
//请求头信息
header: {
name: 'rory',
age: 16
}
}).then(value=>{
console.log(value);
});
}
- axios发送POST请求
//POST请求
btns[1].onclick = function() {
axios.post('http://127.0.0.1:8000/axios', {
//url
p1: {
id: 15,
time: 18
},
//请求头参数
header:{
name: 'jack',
age: 15
}
})
}
- 服务端
//引入express
const express = require("express");
//创建应用对象
const app = express();
//axios
app.all('/axios',(request, response)=>{
response.setHeader('Access-Control-Allow-Origin','*');
response.setHeader('Access-Control-Allow-Headers','*');
const data = {name: 'rory'};
response.send(JSON.stringify(data));
});
//监听端口启动服务
app.listen(8000, ()=>{//两个参数:端口,执行方法
console.log('服务已启动,8000端口监听中');
});
- 发送AJAX请求
//AJAX请求
btns[2].onclick = function() {
axios({
//请求方法
method: 'POST',
//url
url: 'http://127.0.0.1:8000/axios',
//url参数
params: {
vip: 10,
level: 30
},
//头信息
headers:{
a:100,
b:200
},
//请求体参数
data:{
username: 'admin',
password: 'admin'
}
}).then(response=>{
console.log(response);
console.log(response.status);
console.log(response.statusText);
console.log(response.headers);
console.log(response.data);
})
}
服务端与上面一样
13、使用fetch
发送AJAX请求
fetch
属于全局对象,可以直接调用,它的返回结果是一个Promise对象
fetch传参:
fetch('url',options);
//或者
fetch(request(){},options);
//及: fetch()传入两个参数,第一个参数是 url 或者一个request对象,第二个参数是可选项
const btn = document.querySelector('button');
btn.onclick = function() {
fetch('http://127.0.0.1:8000/fetch-server', {
method: "POST",
headers: {
name: "rory"
},
//请求体
body: 'username=rory&passwords=123'
}).then(response=>{
//如果服务器返回的是一个json对象,那么先用json()方法处理为js对象
return response.json();
}).then(response=>{
console.log(response);
})
}
14、跨域相关
14.1、同源策略
同源策略是浏览器的一种安全策略
,AJAX默认遵循同源策略
- 什么是同源?
– 浏览器和服务器 url 的协议
、域名
、端口号
必须完全相同 - 什么是
跨域
?
–违反同源策略
,即上诉三点有不同就叫跨域
14.2、解决跨域
14.2.1、jsonp
一个非官方的跨域解决方案,只支持GET请求
- 工作原理
–利用script标签的跨域能力来发送请求
使用:
用JS创建一个script标签
并把url放进去
var script = document.createElement("script");
script.src = "http://127.0.0.1:5500/ajax%E6%A1%88%E4%BE%8B/GET.html";
//设置回调函数
把script标签添加到body中
document.body.appendChild(script);
服务端:
app.all('/check-username',(request, response)=>{
const data = {
exist: 1,
msg: '用户名已存在'
};
let str = JSON.stringify(data);
response.end(`handle(${str})`);
});
实例:
html和js
用户名:<input type="text" id="username">
<p></p>
<script>
const input = document.querySelector('input');
const p = document.querySelector('p');
//声明handld函数
function handle(data){
input.style.border = "soild 2px #666";
//修改p标签的提示文本
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>
服务端
//引入express
const express = require("express");
//创建应用对象
const app = express();
app.all('/check-username',(request, response)=>{
const data = {
exist: 1,
msg: '用户名已存在'
};
let str = JSON.stringify(data);
response.end(`handle(${str})`);
});
//监听端口启动服务
app.listen(8000, ()=>{//两个参数:端口,执行方法
console.log('服务已启动,8000端口监听中');
});
14.2.1、CORS - 跨域资源共享
官方跨域解决方案
不需要在客户端做任何特殊的操作,完全在服务器
中进行处理。支持get和post请求。CORS新增了一组HTTP首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源
CORS工作原理
通过一个响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应后就会对响应放行
CORS使用
主要就是设置响应头
response.setHeader("Access-Control-Allow-Origin",'*');//允许跨域
实例:
html、css和js
<button id="res">点击发送请求</button>
<style>
#res{
width: 300px;
height: 130px;
border: solid 2px rgb(121, 139, 230);
}
</style>
<script>
const btn = document.getElementById('res');
btn.onclick = function() {
const x = new XMLHttpRequest();
x.open("GET",'http://127.0.0.1:8000/CORS-server');
x.send();
x.onreadystatechange = function() {
if(x.readyState === 4){
if(x.status >= 200 && x.status < 300){
console.log(x.response);
}
}
}
}
</script>
服务端
//引入express
const express = require("express");
//创建应用对象
const app = express();
app.all('/CORS-server',(request,response)=>{
//设置响应头
response.setHeader("Access-Control-Allow-Origin",'*');
response.send('hello CORS');
});
//监听端口启动服务
app.listen(8000, ()=>{//两个参数:端口,执行方法
console.log('服务已启动,8000端口监听中');
});