1 AJAX 简介
AJAX 全称为 Asynchronous JavaScript And XML,就是异步的 JS 和 XML
通过 AJAX 可以在浏览器中向服务器发送异步请求,最大优势为:无刷新获取数据
AJAX 不是新的编程语言,而是一种将现有的标准组合在一起使用的新方式
优点:
- 可以无需刷新页面而与服务器进行通信
- 允许根据用户事件(鼠标、键盘、文档等事件)来更新部分页面内容
缺点:
- 没有浏览历史,不能回退
- 存在跨域问题(同源)
- SEO(搜索引擎优化)不友好
1.1 XML 简介
XML(可扩展标记语言),被设计用来传输和存储数据,XML 和 HTML 类似,不同的是 HTML 中都是预定义标签,而 XML 中没有预定义标签,全都是自定义标签,用来表示一些数据
比如有个学生数据:
name = "孙悟空"; age = 18; gender = "男";
用 XML 表示:
<student>
<name>孙悟空</name>
<age>18</age>
<gender>男</gender>
</student>
但现在已经被 JSON 取代了
用 JSON 表示:
{
"name": "孙悟空",
"age": 18,
"gender": "男"
}
1.2 HTTP 协议请求报文和响应文本结构
HTTP(超文本传输协议),协议详细规定了浏览器与万维网服务器之间相互通信的规则约定
请求报文:
行 POST BV1WC4y1b78y?spm_id_from=3 HTTP/1.1
头 Host:atguigu.com
Cookie:name=guigu
Content-type:application/x-www-form-urlencoded
User-Agent:chrome 83
空行
体 username=admin&password=admin(get请求时,请求体为空;post请求时,请求体可以为空)
响应报文:
行 HTTP/1.1 200 OK
头 Content-type:text/html;charset=utf-8
Content-length: 2048
Content-encoding: gzip
空行
体 <html>
<head></head>
<body>
<h1>尚硅谷</h1>
</body>
</html>
Chrome 网络控制台查看通信报文
1.3 Node.js 安装
去官网找相应版本下载安装即可
检验是否安装成功:命令行中输入 node -v
出现 node 安装版本即可
1.4 express 框架
安装 express 包
npm init --yes
npm i express
express 的基本使用
// 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 端口监听中...');
})
终端输入 node espress基本使用.js
运行
浏览器 http://127.0.0.1:8000/ 看到运行结果:
1.5 nodemon 自动重启工具安装
终端输入:
npm install -g nodemon
终端运行
npx nodemon server.js
2 案例准备
get.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#responseMsg {
width: 200px;
height: 200px;
border: 2px solid peachpuff;
}
</style>
</head>
<body>
<button>点击发送请求</button>
<div id="responseMsg"></div>
</body>
</html>
server.js
// 1. 引入 express
const express = require('express');
// 2. 创建应用对象
const app = express();
// 3. 创建路由规则
// request:对请求报文的封装
// response:对响应报文的封装
app.get('/server', (request, response) => {
// 设置响应头 设置允许跨域
response.setHeader('Access-Control-Allow-Origin', '*');
// 设置响应
response.send('Hello Express');
});
// 4. 监听端口启动服务
app.listen(8000, () => {
console.log('服务器已经启动,8000 端口监听中...');
})
2.1 请求的基本操作
get.html
<script>
// 获取元素
const btn = document.querySelectorAll('button')[0];
const responseMsg = document.querySelector('#responseMsg');
// 绑定事件
btn.addEventListener('click', function() {
// 1. 创建对象
const xhr = new XMLHttpRequest();
// 2. 初始化 设置请求方法和 URL
xhr.open('GET', 'http://127.0.0.1:8000/server');
// 3. 发送
xhr.send();
// 4. 事件绑定 处理服务端返回来的结果
xhr.onreadystatechange = function() {
// 判断(服务器返回了所有的结果)
if(xhr.readyState === 4) {
// 判断响应状态码
// 2xx:成功
if(xhr.status >= 200 && xhr.status < 300) {
console.log(xhr);
responseMsg.textContent = xhr.response;
}
}
}
})
</script>
- onreadystatechange:监听函数,实例的属性发生变化时,就会执行这个函数
- readyState:实例对象的当前状态,通信过程中,每当实例对象发生状态变化,他的属性值就会一直改变。只读且只包含以下五种状态
- 0:XMLHttpRequest 实例已经生成,但是实例的方法还没有被调用
- 1:方法已经调用,但是实例的方法还没有调用,仍然可以使用实例的方法,设定 HTTP 请求的头信息
- 2:实例的方法已经调用,并且服务器返回的头信息和状态码已经收到
- 3:正在接收服务器传来的数据体(body 部分)
- 4:服务器返回的数据已经完全接收,或者本次接收已经失败
- status:服务器回应的 HTTP 状态码
- 200:ok,访问正常
- 301:永久移动
- 302:暂时移动
- 304:未修改
- 307:暂时重定向
- 401:未授权
- 403:禁止访问
- 404:未发现指定网址
- 500:服务器发生错误
- response:服务器返回的数据体(HTTP 回应的 body 部分)
2.2 设置请求参数
// 2. 初始化 设置请求方法和 URL
xhr.open('GET', 'http://127.0.0.1:8000/server?username=andy&password=123');
2.3 发送 Post 请求
html 中发送 POST 请求
// 2. 初始化 设置请求方法和 URL
xhr.open('POST', 'http://127.0.0.1:8000/server?');
server.js 增加 post 请求响应
// 3. 创建路由规则
// request:对请求报文的封装
// response:对响应报文的封装
app.get('/server', (request, response) => {
// 设置响应头 设置允许跨域
response.setHeader('Access-Control-Allow-Origin', '*');
// 设置响应
response.send('Hello Express');
});
app.post('/server', (request, response) => {
// 设置响应头 设置允许跨域
response.setHeader('Access-Control-Allow-Origin', '*');
// 设置响应体
response.send('Hello Ajax Post');
})
- server.js 文件发生改变的时候,需要重新开启服务
2.4 Post 设置请求体
// 3. 发送
// xhr.send('username=andy&password=123');
xhr.send('username:andy&password:123');
2.5 设置请求头信息
html 中设置请求头
// 设置请求头
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.setRequestHeader('name', 'lxlu');
server.js 中设置响应体
// 响应头
response.setHeader('Access-Control-Allow-Headers', '*');
- setRequestHeader():设置浏览器发送的 HTTP 请求的头信息,如果该方法多次调用,设定同一个字段,每次调用的值都会被合并成一个单一的值发送
2.6 服务器响应 JSON 数据
server.js
// 可以接收任意类型的请求
app.all('/json-server', (request, response) => {
// 设置响应头 设置允许跨域
response.setHeader('Access-Control-Allow-Origin', '*');
// 响应头
response.setHeader('Access-Control-Allow-Headers', '*');
// 响应一个数据
const data = {
name: 'andy',
age: 18
};
// 对对象进行字符串转换
let str = JSON.stringify(data);
// 设置响应体
response.send(str);
})
对接收到的 JSON 数据进行手动转换 JSON.parse()
// 发送请求
const xhr = new XMLHttpRequest();
// 初始化
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);
// responseMsg.innerHTML = xhr.response;
// 手动对数据转换
let data = JSON.parse(xhr.response);
responseMsg.innerHTML = data.name;
}
}
}
可以设置响应体数据类型,从而避免手动转换
2.7 IE 缓存问题
IE 缓存问题:IE 浏览器会将 ajax 请求的返回结果作为缓存,如果再遇到相同的 ajax 请求,就直接在缓存中取结果,这样就得不到最新的服务器响应结果(chrom 浏览器可以实时更新)
解决方法:只要每次请求的 url 不一样,IE 就会认为不是同一个请求,就可以重新请求服务器(在 url 后面加上一个时间戳参数)
xhr.open('GET', 'http://127.0.0.1:8000/ie?t=' + Date.now());
2.8 请求超时于网络异常处理
模拟给服务器发送请求超时的情况,3s 后返回响应结果
app.get('/delay', (request, response) => {
// 设置响应头 设置允许跨域
response.setHeader('Access-Control-Allow-Origin', '*');
// 设置响应
setTimeout(() => {
response.send('网络请求超时');
}, 3000);
});
请求超时与网络异常的处理
// 超时设置 2s
xhr.timeout = 2000;
// 超时回调
xhr.ontimeout = function() {
alert('网络异常,请稍后重试');
}
// 网络异常回调
xhr.onerror = function() {
alert('你的网络似乎出了点问题')
}
- timeout:多少毫秒后,如果请求仍然没有得到结果,就会自动终止
- ontimeout:监听函数,如果发生 timeout 事件,就会执行这个监听函数(timeout 的监听函数)
- onerror:请求失败的监听函数
2.9 取消请求
取消请求发生在已经发送了请求,但是还没有返回请求结果,这时可以设置取消请求:
<body>
<button class="send">发送请求</button>
<button class="cancel">取消请求</button>
</body>
<script>
const send = document.querySelector('.send');
const cancel = document.querySelector('.cancel');
let xhr = null; // 注意这里是用 let,不是 const(const 的值是不能改的)
send.addEventListener('click', function() {
xhr = new XMLHttpRequest();
xhr.open('GET', 'http://127.0.0.1:8000/delay');
xhr.send();
})
cancel.addEventListener('click', function() {
xhr.abort();
})
</script>
- abort():终止已经发出的 HTTP 请求,调用这个方法后,readyState 属性变为 4,status 属性变为 0
2.10 重复请求问题
设置一个标识变量 isSending,当点击发送按钮时,判断 isSending 的布尔值,如果为 true,表示正在发送,需要取消请求,创建新的请求
let xhr = null;
// 标识变量
let isSending = false;
send.addEventListener('click', 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;
}
}
})
cancel.addEventListener('click', function() {
xhr.abort();
})
3 jQuery 发送 AJAX 请求
引入 jQuery:(BootCDN --> jquery --> 复制 <script> 标签)
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
get / post 请求: . g e t ( u r l , [ d a t a ] , [ c a l l b a c k ] , [ t y p e ] ) 、 .get(url, [data], [callback], [type])、 .get(url,[data],[callback],[type])、.post(url, [data], [callback], [type])
- url:请求的 URL 地址
- data:请求携带的参数
- callback:载入成功时回调函数
- type:设置返回内容格式,xml、html、script、json、text、_default
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.js"></script>
<title>Document</title>
</head>
<body>
<h2>jQuery发送AJAX请求</h2>
<button>GET请求</button>
<button>POST请求</button>
<button>通用性方法ajax</button>
</body>
<script>
// 发送GET请求
$('button').eq(0).click(function() {
$.get('http://127.0.0.1:8000/jquery-server', {id: 3, name: 'andy'}, function(data) {
console.log(data);
}, 'json');
})
// 发送POST请求
$('button').eq(1).click(function() {
$.post('http://127.0.0.1:8000/jquery-server', {id: 3, name: 'andy'}, function(data) {
console.log(data);
}, 'json');
})
// 利用AJAX发送
$('button').eq(2).click(function(){
$.ajax({
// url
url: 'http://127.0.0.1:8000/jquery-server',
// 参数
data: {id: 3, name: 'andy'},
// 请求类型
type: 'GET',
// 响应体结果
dataType: 'json',
// 成功的回调
success: function(data) {
console.log(data);
},
// 超时时间
timeout: 2000,
// 失败的回调
error: function(){
console.log('出错啦!!')
},
// 头信息
headers: {
a: 121
}
})
})
</script>
</html>
server.js
// jQuery 服务
app.all('/jquery-server', (request, response) => {
response.setHeader('Access-Control-Allow-Origin', '*');
response.setHeader('Access-Control-Allow-Headers', '*');
// response.send('Hello jQuery');
let data = {
id: 3,
name: 'andy'
};
response.send(JSON.stringify(data));
});
4 Axios 发送 AJAX 请求
引入 axios
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
axios.get(url[, config])
axios.post(url[, data[, config]])
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<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>GET请求</button>
<button>POST请求</button>
<button>AJAX请求</button>
</body>
<script>
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: 12,
nickName: 'candy'
},
// 请求头信息
headers: {
name: 'lxlu',
age: 18
}
}).then((value) => {
console.log(value);
});
}
btns[1].addEventListener('click', function() {
// POST 请求
axios.post('/axios-server',{
username: 'admin',
password: 123
}, {
params: {
id: 121,
nickName: 'kun'
},
headers: {
height: 175,
weight: 60
}
})
})
btns[2].onclick = function() {
// axios函数发送AJAX请求
axios({
method: 'POST',
url: '/axios-server',
params: {
vip: 88,
level: 15
},
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>
</html>
server.js
// axios 服务
app.all('/axios-server', (request, response) => {
response.setHeader('Access-Control-Allow-Origin', '*');
response.setHeader('Access-Control-Allow-Headers', '*');
// response.send('Hello jQuery');
let data = {
id: 3,
name: 'andy'
};
response.send(JSON.stringify(data));
});
5 fetch 发送 AJAX 请求
fetch无需安装,是window的内置方法;fetch对象属于全局对象,可以直接调用,它的返回结果是一个 promise 对象,但是请求的数据在第二个 .then
中才能获取,error可以使用catch统一进行处理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button>AJAX请求</button>
</body>
<script>
const btn = document.querySelector('button');
btn.onclick = function(){
fetch('http://127.0.0.1:8000/fetch-server', {
// 请求方法
method: 'POST',
// 请求头
headers: {
name: 'lxlu'
},
// 请求体
body: 'username=admin&password=123'
}).then(response => {
// return response.text();
return response.json();
}).then(response => {
console.log(response);
});
}
</script>
</html>
server.js
// fetch 服务
app.all('/fetch-server', (request, response) => {
response.setHeader('Access-Control-Allow-Origin', '*');
response.setHeader('Access-Control-Allow-Headers', '*');
let data = {
id: 3,
name: 'andy'
};
response.send(JSON.stringify(data));
});