2021/8/6
一、原生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 = 18;gender = "男";
用XML表示:
<student>
<name>孙悟空</name>
<age>18</age>
<gender>男</gender>
</student>
现在已经被 JSON 取代了
用JSON表示:
{"name": "孙悟空", "age": 18, "gender": "男"}
1.3 AJAX的特点
1.3.1 AJAX 的优点
- 可以无需刷新页面与服务器端进行通信
- 允许你根据用户事件来更新部分页面内容
1.3.2 AJAX 的缺点
- 没有浏览历史,不能回退
- 存在跨域问题
- SEO(搜索引擎优化)不友好
二、Express框架的简单介绍(下载完node.js之后才能使用Express)
-
- 在命令行工具中安装 Express
$ npm install express
- 在命令行工具中安装 Express
-
- express的基本使用方式:
// 1. 引入express
const express = require('express');
// 2. 创建应用对象
const app =express();
// 3. 创建路由规则
// request 是对请求报文的封装
// request 是对响应报文的封装
app.get('/', (request, response) => {
//设置响应
response.send('HELLO EXPRESS');
})
// 4. 监听端口启动服务
app.listen(8000, () => {
console.log("服务已经启动,8000端口监听中......");
})
案例分析
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: 200px;
border: solid 1px #90b;
margin-top: 20px;
}
</style>
</head>
<body>
<button>点击发送请求</button>
<div id="result"></div>
</body>
<script>
// 1. 获取button元素
const btn = document.getElementsByTagName('button')[0];
const result = document.getElementById('result');
// 2. 绑定事件
btn.onclick = function () {
// 2.1 创建对象
const xhr = new XMLHttpRequest();
// 2.2 初始化 设置请求方式 和 url
xhr.open('GET', 'http://127.0.0.1:8000/server');
// 2.3 发送
xhr.send();
// 2.4 事件绑定 处理服务器端返回的结果
/*
on when 当...时候
readystate 是 xhr 对象中的属性,表示状态 ,0--表示默认值 1--表示open完成 2--表示send完成 3--表示服务器端返回部分结果 4-表示服务器端返回返回结果
change 改变
*/
xhr.onreadystatechange = function () {
// 2.4.1 判断 (服务器端返回了所有的结果)
if (xhr.readyState === 4) {
// 判断响应状态码 200 404 403 401 500 (补:2字开关都表示成功)
if (xhr.status >= 200 && xhr.status < 300) {
// 处理结果 (包含:行 头 空行 体)
// a. 响应
console.log(xhr.status); // 状态码
console.log(xhr.statusText); // 状态字符串
console.log(xhr.getAllResponseHeaders()); // 所有响应头
console.log(xhr.response); // 响应体
result.innerHTML = xhr.response;
} else {
}
}
}
}
</script>
</html>
当请求方式为 GET 时,它的请求参数可以直接在 url 后面设置,需要在中间加上一个**?,参数之间用&** 隔开
如: xhr.open('GET', 'http://127.0.0.1:8000/server?name=zhangsan&age=18');
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: 200px;
border: solid 1px blue;
}
</style>
</head>
<body>
<div id="result"></div>
</body>
<script>
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');
// 3. 发送请求
xhr.send();
//4 事件绑定----处理服务器端返回的数据
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >=200 && xhr.status < 300) {
result.innerHTML = xhr.response;
}
}
}
})
</script>
</html>
当请求方式为POST 时,它的请求参数需要加上send() 方法中,如xhr.send('name=xyh&age=22');
三、POST 请求体设置
在前面的案例分析中,我们设置了请求行、请求体,也知道了不同请求方式的请求参数的设置,现在来记录一下请求头的设置
setRequestHeader() 可用来设置请求头,
//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('name=xyh&age=22');
四、服务器端传递JSON数据处理
//server.js
app.all('/json-server', (request, response) => {
// 设置响应头 设置允许跨域
response.setHeader('Access-Control-Allow-Origin', '*');
//设置响应体(一个对象)
const datas = {
name: 'xyh'
};
//对对象进行字符串转换(因为send方法不能传递对象)
let str = JSON.stringify(datas);
//设置响应体
response.send(str);
});
对于客户端对服务器端返回的JSON数据有两种处理方式
1. 手对处理
<script>
const result = document.getElementById('result');
// 添加事件
window.onkeydown = function () {
// 1. 新建对象
const xhr = new XMLHttpRequest();
// 2. 初始化
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) {
// 手对更改
let datas = JSON.parse(xhr.response);
result.innerHTML = datas.name;
}
}
}
}
</script>
1. 自动转换
自动转换相对于手动转换要方便很多,只需要设置响应体的数据类型为json
xhr.responseType = 'json';
<script>
const result = document.getElementById('result');
// 添加事件
window.onkeydown = function () {
// 1. 新建对象
const xhr = new XMLHttpRequest();
// 1.1 设置响应体数据的类型
xhr.responseType = 'json';
// 2. 初始化
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) {
// 自动转换
// console.log(xhr.response);
result.innerHTML = xhr.response.name;
}
}
}
}
</script>
五、AJAX 关于请求的常见问题
5.1 AJAX请求超时和网络异常处理
// sever.js
app.get('/delay', (request, response) => {
// 设置响应头 设置允许跨域
response.setHeader('Access-Control-Allow-Origin', '*');
//设置响应体
setTimeout (() => {
response.send('延时响应');
}, 3000)
});
// 请求超时和网络异常.html
btn.addEventListener('click', function () {
const xhr = new XMLHttpRequest();
// 超时2s 设置
xhr.timeout = 2000; //超时2s 自动取消请求
// 超时回调
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;
}
}
}
});
可以看见在sever.js中可以 使用定时器,延时了3秒,html文件中设置了2秒的限制,如果在2秒之内没有完成响应,就取消请求,同时设置了超时回调事件。
5.2 手动取消请求操作
在上面的延时响应中,使用了xhr.timeout = 2000;
来进行自动取消请求操作。这时我们使用abort() 方法来完成手动取消请求
<body>
<button>点击发送请求</button>
<button>取消发送请求</button>
</body>
<script>
const btns = document.getElementsByTagName('button');
let xhr = null;
btns[0].onclick = function () {
xhr = new XMLHttpRequest();
xhr.open('GET', 'http://127.0.0.1:8000/delay');
xhr.send(); // 发送请求
}
btns[1].onclick = function () {
xhr.abort(); // 取消请求
}
</script>
5.3 AJAX 请求重复问题解决
在用户使用过程中,难免会存在重复请求(比如想申请一个页面时,因网络卡顿,过多去点击请求按钮),而这也加重了服务器端的工作。以下用一个标识变量的方法就可以解决这个问题。
<script>
const btns = document.getElementsByTagName('button');
let xhr = null;
// 1. 变量标识
let isSending = false; // 是否正在发送AJAX请求
btns[0].onclick = function () {
// 2. 判断标识变量
if (isSending) xhr.abort(); // 如果正在发送,则取消该请求,创建一个新的请求
xhr = new XMLHttpRequest();
// 3. 修改 标识变量的值
isSending = true;
xhr.open('GET', 'http://127.0.0.1:8000/delay');
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
// 4. 修改标识变量
isSending = false;
}
}
}
</script>
六、jQuery 中发送AJAX请求
在jQuery中发送AJAX有三种方式,分别是get,post, 和一种通用的方式,其中使用回调函数来接收返回的数据
// serve.js
app.all('/jquery-server', (request, response) => {
// 设置响应头 设置允许跨域
response.setHeader('Access-Control-Allow-Origin', '*');
//设置响应体(一个对象)
const data = {name: 'xhy', age: 18};
response.send(JSON.stringify(data));
});
<body>
<button>发送请求GET</button>
<button>发送请求POST</button>
<button>通过请求AJAX</button>
</body>
// crossorigin="anonymous" 解决跨越问题报错
<script crossorigin="anonymous" src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
// 1. get 请求
$('button').eq(0).click(function () {
$.get('http://127.0.0.1:8000/jquery-server', {a:100, b:300}, function (data) {
console.log(data);
},'json') // 以json的方式接收服务器端返回的数据
})
// 2. post 请求
$('button').eq(1).click(function () {
$.post('http://127.0.0.1:8000/jquery-server', {a:100, b:300}, function (data) {
console.log(data);
}) // 若没有设置响应体方式,则默认为字符串格式
})
// 3. 通过方式
$('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: 2000,
// 失败的问题
error: function () {
console.log('出错了!!');
}
})
})
</script>
七、 axios 发送AJAX请求
7.1 (get / post)请求方式
axios支持promise,接收返回数据时使用.then()
<body>
<button>发送请求GET</button>
<button>发送请求POST</button>
<button>AJAX</button>
</body>
<script crossorigin="anonymous" src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
<script>
const btn = document.querySelectorAll('button');
// 配置 baseURL
axios.defaults.baseURL = 'http://127.0.0.1:8000';
btn[0].onclick = function () {
// GET 请求
axios.get('/axios-server', {
// url 参数
params: {
id: 51802121013,
vip: 7
},
// 请求头信息
headers: {
name: 'xyh',
age: 21
}
})
.then(function (data) {
console.log(data);
})
}
btn[1].onclick = function () {
// POST 请求
axios.post('/axios-server', {
// 请求体
username: 'admin',
password: '123456'
},
{
// url 参数
params: {
id: 51802121013,
vip: 7
},
// 请求头信息
headers: {
name: 'xyh',
age: 21
}
})
}
</script>
仔细观察上面的代码,可以发现get请求方式和 post请求方式的格式不一样。经过官方文档的查看,发现post请求方式,如果有请求体(data),则放在url 和 config中间
7.2 axios 通用发送AJAX请求方式
类似于jquery 通用发送AJAX请求方式
btn[2].onclick = function () {
axios({
// 请求方式
method: 'GET',
// url
url: 'http://127.0.0.1:8000/axios-server',
// url 参数
params: {
id: 51802121013,
vip: 7
},
// 请求头信息
headers: {
name: 'xyh',
age: 21
},
// 请求体参数
data: {
username: 'admin',
password: '123456'
}
}).then(res => {
console.log(res);
}).catch(err => {
console.log(err);
})
}
八、跨域
8.1 同源策略(说白了就是在同一个服务器)
同源策略(Same-Origin Policy)最早是由Netscape公司提出的,是浏览器的一种案例策略。
同源: 协议、域名、端口号、必须完全相同
违背同源策略就是跨域
8.2 如何解决跨域
8.2.1 JSONP
1) JSONP 是什么
JSONP(JSON with Padding)是一个非官方的跨域解决方案,纯粹凭程序员的聪明才智开发出来,只支持 get 请求
2)JSONP 怎么工作的?
在网页有一些标签天生具有跨域能力,比如: img link script
JSONP 就是利用 script 标签的跨域能力来发送请求的
3)使用JSONP的使用
<style>
#result {
width: 300px;
height: 100px;
border: solid 1px #78a;
}
</style>
</head>
<body>
<div id="result"></div>
</body>
<script>
//处理数据
function handle(data) {
//获取 result 元素
const result = document.getElementById('result');
result.innerHTML = data.name;
}
</script>
// 这里原本页面的协议是files,而script请求的协议是http,属于跨域问题,但script中天生具有跨域能力
<script src="http://127.0.0.1:8000/jsonp-server"></script>
app.all('/jsonp-server',(request, response) => {
const data = {
name: '尚硅谷atguigu'
};
//将数据转化为字符串
let str = JSON.stringify(data);
//返回结果
response.end(`handle(${str})`);
});
4) JSONP 使用小案例——原生 (判断用户名是否存在,不存在则提醒用户)
<body>
用户名<input type="text" id="username"></input>
<p></p>
<script>
// 1. 获取input元素
const input = document.querySelector('input');
const p = document.querySelector('p');
// 2. 声明handle函数
function handle(data) {
input.style.border = "solid 1px red";
//修改p标签的提示文本;
p.innerHTML = data.msg;
}
// 3. 绑定事件(失去焦点事件)
input.onblur = function() {
// 3.1 获取用户的输入值
let username = this.value;
// 3.2 向服务器端发送请求 检测用户名是否存在
// a.创建script标签
const script = document.createElement('script');
// b.设置标签的src属性
script.src = "http://127.0.0.1:8000/check";
// c.将script插入到文档中 (必不可少)
document.body.appendChild(script);
}
</script>
</body>
app.all('/check',(request, response) => {
// response.send('console.log("hello jsonp")');
const data = {
exist: 1,
msg: '用户名已经存在'
};
//将数据转化为字符串
let str = JSON.stringify(data);
//返回结果
response.end(`handle(${str})`);
});