【AJAX】学习笔记
00- AJAX 介绍
AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。
AJAX 不是新的编程语言,而是一种使用现有标准的新方法。
AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。
AJAX 不需要任何浏览器插件,但需要用户允许 JavaScript 在浏览器上执行。
XMLHttpRequest 只是实现 Ajax 的一种方式。
github学习笔记文件:https://github.com/kurohoneko/web/tree/main/AJAX
1. XML 简介
XML是可扩展标记语言,被设计用来传输数据和存储数据,XML和HTML类似,不同的是HTML中都是预定义标签,而XML中没有预定义标签,全是自定义标签。
也就是说,我的个人信息数据可以用XML表示为:
<my>
<name>一个名字</name>
<age>18</age>
</my>
现在已经被JSON取代了。
2. JSON 简介
JSON 是一种轻量级的数据交换格式,它基于 ECMAScript (w3c制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
用JSON表示:
{"name":"一个名字", "age":18 }
转换:
JSON.stringify(xxx);
转换为字符串,比如用于转换为 JSON格式的字符串。JSON.parse(xxx);
转换为对象,比如转换为 JSON格式的对象。类似于 XML 的特性:
-
JSON 是纯文本
-
JSON 具有“自我描述性”(人类可读)
-
JSON 具有层级结构(值中存在值)
-
JSON 可通过 JavaScript 进行解析
-
JSON 数据可使用 AJAX 进行传输
相比 XML 的不同之处:
-
没有结束标签
-
更短
-
读写的速度更快
-
能够使用内建的 JavaScript eval() 方法进行解析
-
使用数组
-
不使用保留字
3. AJAX 的优点、缺点
优点:
- 无需刷新页面就可以与服务器通信
- 允许根据用户事件(鼠标、键盘、表单等)来更新部分页面内容
缺点:
- 没有浏览历史,不能回退
- 存在跨域问题(同源)
- SEO不友好(搜索引擎 网页爬虫爬不到)
01- HTTP 协议请求报文与响应报文结构
HTTP协议 超文本传输协议,协议规定了浏览器与万维网服务器之间互相通信的规则。
主要规范了两部分内容:请求、响应。
请求报文(客户端向服务器请求)
- 行 GET/POST /s?ie=utf-8 HTTP/1.1
- 头 Host: google.com
User-Agent: chrome 83 ....等等
- 空行
- 体 username=xxx 如果是GET请求就为空,如果是PSOT请求可以不为空(可有可无)
响应报文(服务器发给客户端)
- 行 HTTP/1.1 200(响应状态码,如404 500) OK(响应状态字符串,跟状态码对应,不需要单独设置)
- 头 Content-Type: text/html;charset=utf-8
Content-length: 2048 ....等等
- 空行
- 体 (主要的返回结果)
<html>
<head> </head>
<body> <h1> 字字字</h1> </body>
</html>
02- Chrome 网络控制台查看通信报文
Network,会列出网页加载过程中所有发送的请求。点击 s?ie=utf-8.... 查看内容。
Headers(头) - Preview(响应体预览) - Response(响应体)- Initiatg - Timing - Cookies
【GET请求 与 POST请求】
Headers
-Response Headers (响应头)
点击“view source” 后,显示原始的响应报文,响应行 HTTP/1.1 200 OK 与响应头。响应体在 Response(响应体) 里,显示HTML代码与内联的css样式等
-Request Headers (请求头)
【GET请求】默认不显示请求行,点击 “view source” 后,显示请求行,GET /s?ie=utf-8&...... HTTP/1.1,也就是:方法 + URL + HTTP协议版本。
【POST请求】点击 “view source” 后,显示请求行,显示 POST /login HTTP/1.1
-Query String Parameters(查询字符串参数)
对 URL 当中的参数进行解析,格式化,更加准确地看到参数情况。
-Form Data(请求体)
比如表单,点击提交按钮后,浏览器把HTTP的报文封装好,提交给目标指定服务器的端口。login_name=XXXX&login_password=123456
Preview
响应体预览,对 Response(响应体) 里的代码进行预览,可以看到网页
03- Node.js 安装
AJAX运行应用时需要一个服务端。
https://nodejs.org/zh-cn/download/
然后cmd,node -v 查看是否安装成功与安装的版本
04- Express 框架
这里主要学前端 这个express可以不懂 就把他当成接收返回数据的后端服务器就行了
https://www.expressjs.com.cn/
在主工作文件夹右键,在集成终端中打开,输入如下:
npm init --yes
然后安装Express,还是右键打开终端,输入如下:
npm i express
然后编写 js 文件:
server.js
// 这里主要学前端 这个express可以不懂 就把他当成接收返回数据的后端服务器就行了
// 1. 引入 express
const { response } = require('express');
const express = require('express');
// 2. 创建应用对象
const app = express();
// 3. 创建路由规则
// request 是对请求报文的封装
// response 是对相应报文的封装
// /server 是如果客户端浏览器向服务端请求时,如果url的路径 请求行的第二段内容 的路径是/server的话
// 就会执行回调函数里面的代码,并且由response做出响应
app.get('/server', (request, response) => {
//设置响应头
response.setHeader('Access-Control-Allow-Origin','*');
//设置响应体
response.send('HELLO 你好 AJAX');
});
// 4. 监听端口启动服务
app.listen(8000, ()=>{
console.log("服务已经启动,8000 端口正在监听中...");
} )
然后右键 server.js 文件所在文件夹,打开终端,输入 node 文件名,再打开浏览器 127.0.0.1:8000/server,点击network,刷新查看。
-这个 /server 是什么意思?我初学大概猜测的是,这个路由和交换机路由差不多,如果url是xxx.com/server,那就是去往server那个页面,就执行'/server'的回调函数,来获取相对应的数据。比如,youtube.com/me,路由规则写'/me'并在回调函数里写入我的个人信息数据,好让服务器获取。【2023年2月12日 刚学一会儿】
05- AJAX 请求的基本操作
POST请求和GET请求大致相同。
1. GET 请求
编写客户端HTML文件:
客户端HTML代码.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: solid 1px #f00;
}
</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');
// 3. 发送
xhr.send();
// 4. 事件绑定 处理服务端返回的结果
// on 当...的时候
// readystate 是 xhr 对对象中的属性,表示状态 0 1 2 3 4
// 0 未初始化 1 open方法调用完毕 2 send方法调用完毕 3 服务端返回部分结果 4 服务端返回所有结果
// change 改变
xhr.onreadystatechange = function() {
// 判断(服务端返回了所有的结果)
if(xhr.readyState === 4){
// 判断响应状态码 200 404 500 ....
// 状态码只要是2xx 都是成功 所以范围在200到299
if(xhr.status >= 200 && xhr.status < 300){
// 处理结果 行 头 空行 体
// 1. 响应行
console.log(xhr.status); //状态码 200 404等
console.log(xhr.statusText); //状态字符串 OK
console.log(xhr.getAllResponseHeaders()); //所有的响应头
console.log(xhr.response); //响应体
// 把响应结果呈现在div中
result.innerHTML = xhr.response;
}else{
}
}
}
}
</script>
</body>
</html>
设置请求方法和url,xhr.open('GET', 'http://127.0.0.1:8000/server?name=q&pid=12345');
然后把 server.js 文件在终端运行后,打开html文件,点击发送请求按钮,div显示了 “HELLO 你好 AJAX”。可network刷新查看发送的请求报文。
2. AJAX 设置请求参数
在 url 后面添加参数,以 ? 号分隔,加参数名字与参数值,如有多个值,用 & 符号分隔。
例如name是q,ipd是12345,那么就是:xhr.open('GET', 'http://127.0.0.1:8000/server?name=q&pid=12345')
3. POST 请求
编写HTML,把GET改为POST。不建议使用GET发送请求体body,建议POST。HTTP 协议没有为 GET 请求的 body 赋予语义,也就是即不要求也不禁止 GET 请求带 body。HTTP GET 请求可以有 body 吗?
情景:当把鼠标放到div处时,发送POST请求。
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>AJAX 发送 POST 请求</title>
<style>
#result{
width: 200px;
height: 100px;
border: solid 1px #f00;
}
</style>
</head>
<body>
<div id="result"></div>
<script>
// 获取元素对象
const result = document.getElementById("result");
// 绑定事件
result.addEventListener("mouseover", function() {
console.log("鼠标移动至div了");
// 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>
</body>
</html>
之前的 server.js 文件只需要添加 post 路由规则:(其中post不要大写!xxx.post 是正确的)
server.js 添加psot路由规则
// 这里主要学前端 这个express可以不懂 就把他当成接收返回数据的后端服务器就行了
// 1. 引入 express
const { response, request } = require('express');
const express = require('express');
// 2. 创建应用对象
const app = express();
// 3. 创建路由规则
// request 是对请求报文的封装
// response 是对相应报文的封装
// /server 是如果客户端浏览器向服务端请求时,如果url的路径 请求行的第二段内容 的路径是/server的话
// 就会执行回调函数里面的代码,并且由response做出响应
app.get('/server', (request, response) => {
//设置响应头
response.setHeader('Access-Control-Allow-Origin','*');
//设置响应体
response.send('HELLO 你好 AJAX');
});
// 3.2 创建路由规则 POST
app.post('/server', (request, response) => {
//设置响应头
response.setHeader('Access-Control-Allow-Origin','*');
//设置响应体
response.send('HELLO AJAX 你好 post');
});
// 4. 监听端口启动服务
app.listen(8000, ()=>{
console.log("服务已经启动,8000 端口正在监听中...");
} )
4. POST 设置请求体
在发送post请求时,xhr.send(); 里面设置请求体内容,如:
// 3. 发送
xhr.send('a=100&b=200');
请求体的格式内容非常灵活,可以是 a=100&b=200&c=300
也可以是 a:100&b:200&c:300
甚至是 12312131254
。但是不可以随便写,必须让服务端可以处理可以解析,在语法上可以随意写,但实际应用中最好按照特定格式: a=100&b=200&c=300
。
5. AJAX设置请求头
设置请求头需要在 请求行 的下面,发送 的上面,也就是在open后面加一个方法调用就可以了。
这个方法是专门设置请求头的: xxx.setRequestHeader('','');
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
application/x-www-form-urlencoded
是浏览器默认的编码格式
也可以自定义请求头,其他类型的请求头,比如: xxx.setRequestHeader('name','qqqq');
但是在浏览器查看会报错,标红,因为name不是预定义的头,而Content-Type就是预定义的,
如果想发自定义请求头,需要在路由规则里设置请求头,response.setHeader('Access-Control-Allow-Headers','*');
所有类型的头信息都可以接收
// 3.2 创建路由规则 POST
app.post('/server', (request, response) => {
//设置响应头
response.setHeader('Access-Control-Allow-Origin','*');
//响应头 所有类型的头信息都可以接收
response.setHeader('Access-Control-Allow-Headers','*');
//设置响应体
response.send('HELLO AJAX 你好 post');
});
但,还是不行,把post改成all,不管是get、post、什么类型的请求都可以
all 可以接收任意类型的请求 不管是get 还是post 还是什么的都可以
// 创建路由规则 all 可以接收任意类型的请求
app.all('/server', (request, response) => {
//设置响应头
response.setHeader('Access-Control-Allow-Origin','*');
//响应头 所有类型的头信息都可以接收
response.setHeader('Access-Control-Allow-Headers','*');
//设置响应体
response.send('HELLO AJAX 你好 post');
});
6. option 请求 (预检测)
option请求就是一次效验,后面还有一次才是真正的请求。
如果校验请求没有得到对应的结果,然后前端就不能发送 post 请求了。简单来说就是没有 potion路由,app.post()不行,改为 app.all();
就可以了,可以接收任意类型的请求。
7. 服务端响应 JSON 数据
这次把url改成json-server,相应的服务端也得改。
客户端html:
客户端 - JSON 响应
<!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>JSON 响应</title>
<style>
#result{
width: 200px;
height: 100px;
border: solid 1px #f00;
}
</style>
</head>
<body>
<div id="result"></div>
<script>
const result = document.getElementById("result");
//绑定键盘按下事件
window.onkeydown = function(){
//发送请求
const xhr = new XMLHttpRequest();
//设置响应体数据的类型(2.自动转换)
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){
// 把响应体内容数据 输出给div显示
//result.innerHTML = xhr.response;
// 1. 手动对数据转换
//let data = JSON.parse(xhr.response);
//console.log(data); //data从字符串转换为对象
//result.innerHTML = data.name;
// 2. 自动转换
console.log(xhr.response); //对象
result.innerHTML = xhr.response.name;
}
}
}
}
</script>
</body>
</html>
服务端路由规则 /json-server 。
服务端js:
服务端 js代码
// 1. 引入 express
const { response, request } = require('express');
const express = require('express');
// 2. 创建应用对象
const app = express();
// 3.4 创建路由规则 json-server
app.all('/json-server', (request, response) => {
//设置响应头
response.setHeader('Access-Control-Allow-Origin','*');
//响应头 所有类型的头信息都可以接收
response.setHeader('Access-Control-Allow-Headers','*');
//响应一个数据
const data = {
name: 'hhhhhhhhhh'
};
//对对象格式data进行转换为字符串
let str = JSON.stringify(data);
//设置响应体
//response.send('HELLO AJAX 你好 json-server');
response.send(str);
});
// 4. 监听端口启动服务
app.listen(8000, ()=>{
console.log("服务已经启动,8000 端口正在监听中...");
} );
其中,服务端响应体与客户端有不同的代码,如下:
服务端响应体: response.send('HELLO AJAX 你好 json-server');
客户端把响应体内容输出显示: result.innerHTML = xhr.response;
,可以直接输出内容到div 显示
如果涉及多个数据,比如对象的属性名与属性值,不能直接传递过去,因为 响应体 response.send() 只能传输字符串,所以需要转换,字符串转对象,对象转字符串。
【服务端】:
//响应一个数据
const data = {
name: 'hhhhhhhhhh'
};
//对对象格式data进行转换为字符串
let str = JSON.stringify(data);
//设置响应体
response.send(str);
【客户端】:
// 1.方法一: 手动对数据转换 // 手动转换时,和自动转换 let str = JSON.stringify(data); 冲突
let data = JSON.parse(xhr.response); // xhr.response 从字符串转换为对象
console.log(data); // 显示 {"name":"hhhhhhhhhh"}
result.innerHTML = data.name; // 显示 hhhhhhhhhh
// 2.方法二: 自动转换
console.log(xhr.response); // 已经被上面的 let str = JSON.stringify(data); 转换为了对象格式
result.innerHTML = xhr.response.name; // 显示 hhhhhhhhhh
06- nodemon 自动重启服务工具 安装及使用
每当修改服务端 js 代码时,都需要手动 ctrl + c 来停止服务,再 node xxx.js 启动服务。安装 nodemon 可以自动重启服务。
打开 npm官网 https://www.npmjs.com/ ,搜索 nodemon,点击 nodemon详情页, https://www.npmjs.com/package/nodemon
右键打开终端,输入命令安装nodemon:
npm install -g nodemon
安装完了之后,即可运行 js 代码。例如:
nodemon 文件名
// 如果提示无法运行加载,则在前面加上 npx
npx nodemon 文件名
现在开始,每当对代码做出修改保存,它都会自动重启。
07- IE 浏览器缓存问题(2022-6-16 IE停用)
每次请求时,ie浏览器会有缓存问题,假如服务端更新数据,客户端可能获取的还是老的数据(缓存)。
所以,在 url 后面添加参数 时间戳,每次请求时,参数都是不一样的(时间戳),所以ie会调用服务端,以便获取最新数据。
// 初始化 设置类型与url,在后面加上参数 时间戳
xhr.open('POST', 'http://127.0.0.1:8000/ie?t=' + Date.now() );
08- 请求超时与网络异常处理
延时请求、请求超时、网络异常。
客户端:
// 给按钮绑定事件
btn.addEventListener('click', function(){
//创建对象
const xhr = new XMLHttpRequest();
//超时设置 2s 2秒
xhr.timeout = 2000; //canceled 取消
//超时回调
xhr.ontimeout = function(){
alert("请求超时,请稍后重试");
}
//网络异常回调
xhr.onerror = function(){
alert("您的网络似乎出了一些问题");
}
//初始化 请求类型与url
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;
}
}
}
});
服务端js:
// 3.5 延时响应
app.get('/delay', (request,response) => {
//设置响应头 允许跨域
response.setHeader('Access-Control-Allow-Origin','*');
//响应数据
setTimeout(() => {
response.send("延时响应");
}, 3000)
});
【延时】 单独给服务端的响应体设置 3000ms 的延时,点击按钮请求后,等待3秒后请求数据成功。
【超时】客户端 xhr.timeout = 2000; //超时设置 2秒
,只要请求等待时间过了2秒,就出现 canceled 取消请求,因为服务器是延时3秒,所以请求失败。
【超时回调】设置超时回调,当超时了,就会触发回调函数。 xhr.ontimeout = function(){}
【异常回调】网络异常时,触发回调函数。 xhr.onerror = function(){}
09- 取消请求
1.取消请求
AJAX的取消请求,手动在请求的过程中,在结果还没回来之前,取消请求。
xhr.abort();
执行此语句,取消当前请求(在结果返回前)。// abort 取消请求
btns[1].onclick = function(){
xhr.abort(); //取消请求
}
2.请求重复发送 问题
假如用户疯狂点击请求按钮,这对服务器增加的不必要的负载和消耗。所以,点第一次请求时,接着点了第二次请求,就立马取消第一次的,如果连点七次,就取消前六次的。
服务端js:
延时 3秒 的响应
// 延时 3秒 响应
app.get('/delay', (request,response) => {
//设置响应头 允许跨域
response.setHeader('Access-Control-Allow-Origin','*');
//响应数据
setTimeout(() => {
response.send("延时响应");
}, 3000);
})
客户端:
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; // 相当于节流阀,true,是的,正在发送AJAX请求
xhr.open("GET",'http://127.0.0.1:8000/delay');
xhr.send();
xhr.readyStatechange = function(){
// 状态码===4,代表服务器已经返回全部结果,然后修改节流阀为false状态
if(xhr.readyState === 4){
// 修改标识变量
isSending = false;
}
}
}
意思就是,先声明一个标识变量 isSending = false 表明是否正在发送请求,然后判断这个标识变量,如果 if(false) 则不执行 abort 取消,然后修改 isSending = true (此时正在发送请求),接着判断 xhr.readyStatechang 状态码 全等于 4,如果成立,也就是服务器返回了全部结果,最后修改标识变量 isSending = false,表明此时没有正在发送请求。
如果正在发送,则isSending为true,直接 if(true) xhr.abort() 取消该请求,创建一个新的请求。
10- jQuery发送AJAX请求
如果没有jQuery本地文件,可以下载一个或引用公共cdn 点击https://www.bootcdn.cn/jquery/
三个方法:get、post、ajax。
$.get('http://127.0.0.1:8000/server', {a:100,b:200}, function(my_data) {}, 'json' );
每种方法都是接收四个参数,第一个参数是给谁发,第二个是发什么参数,第三个是回调函数,第四个是设置响应体什么类型的。也可以只接收三个参数。
GET请求 $.get() :
$('button').eq(0).click(function(){
$.get('http://127.0.0.1:8000/jquery-server', {a:100,b:200}, function(data){
console.log(data);
}); // 显示的是字符串类型的
});
// jQuery发送AJAX请求
app.all('/jquery-server', (request,response) => {
response.setHeader('Access-Control-Allow-Origin','*');
response.send("Hello jQuery AJAX");
});
function(data){} 回调函数里的参数 data 是响应体,可以做一些操作。
POST请求 $.post():
$('button').eq(1).click(function(){
$.post('http://127.0.0.1:8000/jquery-server', {a:100,b:200}, function(data){
console.log(data);
}, 'json'); // 设置响应体类型 json 。结果显示的就是对象
});
// jQuery发送AJAX请求
app.all('/jquery-server', (request,response) => {
response.setHeader('Access-Control-Allow-Origin','*');
const data = {name:'啊啊啊啊'};
response.send(JSON.stringify(data)); // 把对象 转换为 字符串型
});
服务端先把对象转换为字符串型,然后客户端接收到的是字符串类型的。设置了响应体类型 'json' ,然后客户端接收到的就是对象类型的了。
简单来说,不加 json 接收的是字符串,加了 json 接收的就是对象。
get 和 post 请求的区别,在浏览器显示get是字符串,post是表单,post的请求参数不在url后面,在payload的Form Data。
AJAX请求 $.ajax():
自定义程度比较强的可以使用 AJAX 请求,简单简约的可以使用 GET POST。
1.基本的
客户端:
$('button').eq(2).click(function(){
//$.ajax({ // 参数 是对象形式的 });
$.ajax({
// url
url: 'http://127.0.0.1:8000/jquery-server',
// 参数
data: {a:100,b:200,c:'三百多'},
// 请求类型
type: 'GET',
// 成功的回调
success: function(data){
console.log(data);
}
});
});
服务端:
// jQuery发送AJAX请求
app.all('/jquery-server', (request,response) => {
response.setHeader('Access-Control-Allow-Origin','*');
response.send("Hello jQuery AJAX");
});
2. 更多的自定义
客户端:
$('button').eq(2).click(function(){
$.ajax({
// url
url: 'http://127.0.0.1:8000/jquery-server',
// 参数
data: {a:100,b:200,c:'三百多'},
// 请求类型
type: 'GET',
// 响应体结果类型
dataType: 'json',
// 成功的回调
success: function(data){
console.log(data);
},
// 超时时间
timeout: 2000,
// 失败的回调
error: function() {
alert("出错啦!");
},
// 头信息
headers: {
aaa: 300,
aab: 333333
}
});
});
服务端:
// jQuery发送AJAX请求
app.all('/jquery-server', (request,response) => {
response.setHeader('Access-Control-Allow-Origin','*');
response.setHeader('Access-Control-Allow-Headers','*');
const data = {name:'啊啊啊啊'};
setTimeout(() => {
response.send(JSON.stringify(data));
}, 3000);
});
post请求是增加的功能 第二个参数应该是添加的新数据 第三个参数是设置请求报文
11- Axios 发送AJAX请求
axios目前是前端最热门的AJAX工具,vue 与 react 推荐的AJAX请求的工具包。
axios github: https://github.com/axios/axios
或者引用国内cdn:https://www.bootcdn.cn/axios/
1. get请求 axios.get(url [,config] )
axios.get(url [,config] )
,url 与 其他配置axios.get( url... ).then( value... );
//GET请求
axios.get('http://127.0.0.1:8000/axios-server', {
//url参数
params: {
id:100,
vip:7
},
//config 请求头信息
headers: {
name:'xxxx',
n_age:20
}
}).then(value => {
console.log(value); // axios与jQuery不一样,jQuery返回数据用回调函数。
});
app.all('/axios-server', (request,response) => {
response.setHeader('Access-Control-Allow-Origin','*');
response.setHeader('Access-Control-Allow-Headers','*');
response.send("Hello AJAX Axios");
});
2. psot请求 axios.post(url [, data [,config]] )
axios.post(url [,data [,config]] )
,url 、 请求体、其他配置。axios.defaults.baseURL = 'http://127.0.0.1:8000';
//配置 baseURL
axios.defaults.baseURL = 'http://127.0.0.1:8000';
axios.post('/axios-server', {
// data 请求体
username: 'admin XXXX',
userpasw: '1234567890'
}, {
// config 其他参数
//url
params: {
id:200,
vip:999
},
//请求头参数
headers: {
height: 290,
weight: 290
}
})
3. axios 的通用方式请求 axios()
axios(url [,config])
btns[2].onclick = function(){
axios({
//请求方法
method: 'POST',
//url
url: '/axios-server',
//url参数
params: {
pid:1234,
pname:12121212
},
//头信息
headers: {
aaa:100,
abb:200
},
//请求体参数
data: {
username: 'admin',
userpasd: 'admin'
}
}).then(response => { // 返回数据 和 处理
console.log(response);
//响应状态码
console.log(response.status);
//响应状态字符串
console.log(response.statusText);
//响应头信息
console.log(response.headers);
//响应体
console.log(response.data);
})
};
12- fetch 函数 发送AJAX请求
fetch(url [,config])
Response
对象。// fetch(url [,config] )
btn.onclick = function(){
fetch('http://127.0.0.1:8000/fetch-server', {
//请求方法
method: 'POST',
//请求头
headers: {
name: 'HHHHHHHHHHHHHH'
},
//请求体
body: 'username=admin&password=admin'
}).then(response => {
//返回结果
console.log(response);
//获取响应体body,得使用返回结果里面的方法 __proto__ 里的 text() 或 json(),来获取请求体body的数据!
//用return返回
return response.text();
//return response.json(); //如果服务端返回结果是 json,那就把方法换成 .json(),会解析成js对象。
}).then(response => {
console.log(response);
})
}
服务端:
服务端 js代码
// fetch() 发送AJAX请求
app.all('/fetch-server', (request,response) => {
response.setHeader('Access-Control-Allow-Origin','*');
response.setHeader('Access-Control-Allow-Headers','*');
response.send("Hello AJAX fetch()");
});
预览:
URL也可以修改,参数可以直接在后面添加:
fetch('http://127.0.0.1:8000/fetch-server?userid=333', {....
,参数是 userid=333
如果服务端返回的数据是 json 对象,只需要把 return response.text();
的 text()
方法换成 json()
方法:response.json();
13- 同源策略
字面意思,其实说白了,就是来自同一个服务。同一个树根。
比如:这个页面是从127.0.0.1:9000来的,等会请求的数据也是127.0.0.1:9000里的,那么,这两个资源是同源的。这个请求也是满足同源策略的。
服务端 js代码
const express = require('express');
const app = express();
app.get('/home', (request,response) => {
//响应一个页面
response.sendFile(__dirname + '/index.html');
//相当于 127.0.0.1:9000 这个本地文件所属目录
// __dirname :用来动态获取当前文件模块所属目录的绝对路径
// __filename:用来动态获取当前文件的绝对路径
});
app.get('/data', (request,response) => {
//响应用户数据
response.send("这个是用户数据");
});
app.listen(9000, () => {
console.log("服务已启动...9000");
})
客户端:
index.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</title>
</head>
<body>
<h1>AJAX 同源策略</h1>
<h1>其实说白了,就是来自同一个服务。</h1>
<h2>这个页面是从127.0.0.1:9000来的,等会请求的数据也是127.0.0.1:9000里的</h2>
<h2>所以说,这两个资源是同源的。这个请求也是满足同源策略的</h2>
<button>点击获取</button>
<script>
const btn = document.querySelector('button');
btn.onclick = function(){
const xhr = new XMLHttpRequest();
//这里因为是满足同源策略,所以 url 可以简写
xhr.open("GET",'/data');
//发送
xhr.send();
//绑定事件
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
console.log(xhr.response);
}
}
}
}
</script>
</body>
</html>
然后在浏览输入 http://127.0.0.1:9000/home,会直接打开这个 index.html 文件,如图:
点击获取:
14- jsonp 跨域解决方案 实现原理
举例:之前讲 jQuery发送AJAX请求 时,我引用了网络上的jQuery文件:
<script crossorigin="anonymous" src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.3/jquery.js"></script>
现在来说,我本地网页的url 与 引用的网页url 完全不一样,协议不一样,域名不一样,端口不一样。所以说这是一个跨域请求,但是!这个跨域请求丝毫没有影响到代码的运行!还可以正常使用里面的方法等等。所以说,script标签本身就具备跨域特性。
之前在服务端里写的 response.setHeader('Access-Control-Allow-Headers','*');
,就是允许跨域的。
原理演示:
index.html:
!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>jsonp的实现原理</title>
</head>
<body>
<script src="./app.js"></script>
</body>
</html>
app.js:
const data = {
name: '一段文字'
};
console.log(data.name);
首先index.html引入外部资源 js 文件(本地的),右键以 open with Live Server 来打开 index.html(http 协议) 后,发现可以输出js里的结果。
现在查看引用的js文件的url,变成了http协议的:http://127.0.0.1:5501/1st/AJAX/8-jsonp-AJAX/app.js
然后把 index 文件里的 script 的 src 换成完整的书写方式:http://127.0.0.1:5501/1st/AJAX/8-jsonp-AJAX/app.js
然后右键 Open In Default Browser,以本地协议的方式打开 index.html网页,file:///E:/Code/A2/1st/AJAX/8-jsonp-AJAX/jsonp%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86.html
就是以本地协议file方式打开网页,里面引用http协议的资源,发现还是可以正常运行,没有跨域问题。
在此基础上,修改 js 文件代码:
// 1 数据
const data = {
name: '你的名字'
};
//console.log(data.name);
// 2 函数的调用
My_name_is(data);
html文件代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>jsonp的实现原理</title>
<style>
#result{
height: 200px;
width: 300px;
font-size: 50px;
font-weight: bold;
color: rgb(241, 73, 43);
border: 5px solid aqua;
}
</style>
</head>
<body>
<div id="result"></div>
<script>
//处理数据 3.函数
function My_name_is(data){
//获取 result 元素
const result = document.getElementById("result");
result.innerHTML = data.name;
}
//小贴士:预编译变量声明和函数声明会提到最前面
</script>
<!-- <script src="./app.js"></script> -->
<script src="http://127.0.0.1:5501/1st/AJAX/8-jsonp-AJAX/app.js"></script>
</body>
</html>
可以正常运行,可以看到这个 js文件返回的结果是一个数据和一个函数的调用:
这里注意,这个函数前端必须要提前声明,要不然回来之后没有这个函数就会报错,没函数就不能调用。
<script></script>标签发送请求,返回的结果,必须可以让 js 引擎做解析,如果是字符串,那就解析不了了,举例:
前端:
<script src="http://127.0.0.1:8000/jsonp-server"></script>
后端:
// jsonp 服务
app.all('/jsonp-server', (request,response) => {
response.send('hello jsonp-server');
});
运行后,Console 栏报错提示:Uncaught SyntaxError: Unexpected identifier (未捕获的语法错误:意外的标识符)。也就是解析不了,因为这是非 js代码,返回 js代码就可以解析了。
可以看到 Network 栏的 Response 有返回的数据:hello jsonp-server。
返回 js 语句代码:
app.all('/jsonp-server', (request,response) => {
//response.send('hello jsonp-server'); // 返回非 js代码,script标签就无法解析
response.send(' console.log("hello jsonp-server"); ');
});
运行后,可以看到 Console 栏显示 hello jsonp-server,无报错。Response 栏显示: console.log("helo jsonp-server");
所以说,<script>标签请求获取后,要的是函数调用的内容,也就是一段 js代码,js语句。 这样的话,前端的浏览器才可以解析并执行里面的内容。
——————
修改后端服务端代码:
app.all('/jsonp-server', (request,response) => {
//response.send('hello jsonp-server'); // 返回非 js代码,script标签就无法解析
//response.send(' console.log("helo jsonp-server"); ');
//1.数据
const data = {
name: '玻璃柠檬'
};
//将数据转化为 字符串
let str = JSON.stringify(data);
//返回结果 end()不会返回特殊响应头
response.end(`My_name_is(${str})`)
});
打开页面,页面接收到了数据并执行了js代码:My_name_is(${str}) ,返回结果:My_name_is({"name":"玻璃柠檬"})
前端网页预览:
返回的结果的形式,是一个函数的调用,而函数的实参,就是要返回客户端结果的数据。
这个函数必须要在前端声明,要不然回来之后没有这个函数,就会报错。
小贴士: ` ` 是 模板字符串,而里面的 ${ } 是与字符串区分开来的,里面是 变量名 或 JavaScript表达式。
jsonp 实践 - 返回 js 代码
在输入框input输入用户名,输入完后,失去焦点(点击别处),然后向服务端发送请求,对用户名进行是否存在的检测,然后返回结果(用户名已经存在),再让input输入框的颜色变红,
前端 客户端:
<!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> </head> <body> 用户名:<input type="text" id="username"> <p></p> <script> //获取 input 元素 const input = document.querySelector('input'); const p = document.querySelector('p'); //声明 Check_username 函数 function Check_username(data){ //输入用户名的边框变红色 input.style.border = "solid 1px #f00"; //修改 p 标签的提示文本 p.innerHTML = data.msg; }; //绑定事件 onblur失去焦点 input.onblur = function(){ //获取用户的输入值 let username = this.value; //向服务器端发送请求 检测用户名是否存在 // 1. 创建 script 标签 const script = document.createElement('script'); // 2. 设置标签的 src 属性 script.src = 'http://127.0.0.1:8000/jsonp-check-username'; // 3. 将 script 插入到文档中 document.body.appendChild(script); } </script> </body> </html>
后端 服务端:
// jsonp 服务 检测用户名字是否存在 app.all('/jsonp-check-username', (request,response) => { const data = { exist: 1, msg: '用户名已经存在,请换一个别的名字' }; let str = JSON.stringify(data); //转换字符串 response.end(`Check_username( ${str} )`); //返回 js代码 Check(str); });
用户输入完用户名,失去焦点后,获取用户输入的值,准备向服务端发送请求,创建 <script> 标签,设置 script 标签的 src 属性,将 script 标签插入到页面中。这时,页面已经有了 script 标签并发送了请求,服务端返回一个 js语句代码:Check_username(str);
,str 是一个对象,就是服务端的 data 对象,但是它以json格式的字符串形式发给客户端,到客户端后 script 代码自动解析为json对象,然后客户端 Check_username(data) 函数被调用,实参str 传给形参data,然后 data.msg,得到 '用户名已经存在,请换一个别的名字' 。
jQuery 发送 jsonp 请求
语法:$.getJSON('url', function(data){ });
注意:在使用 jQuery 发送 jsonp 请求时,url 后面要补一个参数 callback ,它的值是 ? ,写法:callback=?
完整的就是 http://127.0.0.1:8000/jquery-jsonp-server?callback=?
发送请求,查看已发送请求里面的 callback 参数:
$.getJSON('http://127.0.0.1:8000/jquery-jsonp-server?callback=?', function(data){ console.log(data); });
可以看到,原本的 callback=? 并没有值,但发送请求后,callback是有值的,变成了 jQuery36307196822213934448_1676633833786
服务端可以接收到这个参数,然后把这个 callback 的值,作为函数调用的一个函数名,然后去拼接字符串,
也就是说,现在 jQuery 已经注册了一个名字叫 jQuery36307196822213934448_1676633833786 的函数,
只要回来之后,返回的是 jQuery36307196822213934448_1676633833786 函数的调用,前端就可以对这个数据做一个处理。
说白了,这一串数字,就是一个函数名,好比我在后端写一个 jQuery36307196822213934448_1676633833786( str ),就是调用了这个函数,里面实参是 str,但这个函数是 jQuery 发送请求而声明的,可以简单理解为:
$.getJSON(url, function() {} );,里的回调函数 function(){} 就是 callback 的 值,就是 jQuery36307196822213934448_1676633833786。只需要在服务端调用并返回就可以。
然后后端获取 callback 的值,也就是获取到这个函数名。就可以作为回调函数,来做处理。
app.all('/jquery-jsonp-server',(request,response) => {
let cb = request.query.callback;
response.end(cb);
});
获取到 callback 的值,然后拼接字符串,调用函数:
模板字符串 `${cb}( ${str} )`,相当于 getMax(data) ,把俩字符串拼成函数和实参,调用函数。
app.all('/jquery-jsonp-server',(request,response) => {
const data = {
name: '语言等级考场',
city: ['北京','大连','沈阳']
};
let str = JSON.stringify(data); //转换为字符串
let cb = request.query.callback; //获取 callback 参数的值
//返回结果
response.end(`${cb}( ${str} )`);
//resp...end(`getMax( ${str} )`); //上一句的意思类似于这样: 函数名(实参)
});
客户端:
<body>
<button>jQuery 点击发送 jsonp 请求</button>
<div id="result">
</div>
<script>
$('button').eq(0).click(function(){
$.getJSON('http://127.0.0.1:8000/jquery-jsonp-server?callback=?', function(data){
//callback 的值,是jQuery随机生成的回调函数名字
//callback 作为一个函数的调用
console.log(data);
$('#result').html(`
名称:${data.name}, <br/>
地区:${data.city}
`);
});
});
</script>
</body>
完整客户端:
完整客户端
<!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>jQuery 发送 jsonp 请求</title>
<script crossorigin="anonymous" src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.3/jquery.js"></script>
</head>
<body>
<button>jQuery 点击发送 jsonp 请求</button>
<h2>$.getJSON(url, function() {} );,里的回调函数 function(){} 就是 callback 的 值,就是 jQuery36307196822213934448_1676633833786。只需要在服务端调用并返回就可以。</h2>
<div id="result">
</div>
<script>
$('button').eq(0).click(function(){
$.getJSON('http://127.0.0.1:8000/jquery-jsonp-server?callback=?', function(data){
//$.getJSON(url, function() {} );,里的回调函数 function(){} 就是 callback 的 值,就是 jQuery36307196822213934448_1676633833786。只需要在服务端调用并返回就可以。
//callback 的值,是jQuery随机生成的回调函数名字
//callback 作为一个函数的调用
console.log(data);
$('#result').html(`
名称:${data.name}, <br/>
地区:${data.city}
`);
});
});
</script>
</body>
</html>
主要服务端部分:
服务端核心
// (9).jQuery发送jsonp请求
app.all('/jquery-jsonp-server',(request,response) => {
const data = {
name: '语言等级考场',
city: ['北京','大连','沈阳']
};
let str = JSON.stringify(data);
//接收 callback 参数
//请求url参数中的callback参数的值 赋值给 cb,callback的值是随机的,如jQuery363008071787967251609_1676631158208。
//callback的值也就是回调函数的名字,不过这个值就是jQuery随机生成的。
let cb = request.query.callback;
//返回结果
//resp...end(`getMax( ${str} )`);
response.end(`${cb}( ${str} )`);
//response.end(`${cb}( )`);
});
15- 设置CORS响应头实现跨域
CORS 跨域资源共享。 跨源资源共享(CORS) - HTTP | MDN (mozilla.org)
CORS 是官方的跨域解决方案。不需要客户端做任何操作,完全在服务端进行。支持 GET POST。
如何工作?
只需要服务端设置响应头就可以了。
服务端:
// (10).设置CORS响应头实现跨域
app.all('/cors-server',(request,response) => {
//设置响应头
response.setHeader("Access-Control-Allow-Origin","*"); //允许跨域
response.setHeader("Access-Control-Allow-Origin","http://127.0.0.1:6666"); //只有这个网页才可以向服务端发送请求。
response.setHeader("Access-Control-Allow-Headers","*"); //允许头信息自定义
response.setHeader("Access-Control-Allow-Method","*"); //请求方法随意
response.send("hello cors");
});
。
客户端:
<button>点击发送请求</button>
<h1 id="result"></h1>
<script>
const btn = document.querySelector('button');
btn.onclick = function(){
// 1.创建对象
const xhr = new XMLHttpRequest();
// 2.初始化设置
xhr.open("GET","http://127.0.0.1:8000/cors-server");
// 3.发送
xhr.send();
// 4.绑定事件
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
// 输出响应
console.log(xhr.response);
}
}
}
}
</script>
。
学习资源:https://www.bilibili.com/video/BV1WC4y1b78y