原生 AJAX
AJAX 简介
AJAX 全称为 Asynchronous JavaScript And XML,就是异步的 JS 和 XML。
通过 AJAX 可以在浏览器中向服务器发送异步请求,最大的优势:无刷新获取数据。
AJAX 不是新的编程语言,而是一种将现有的标准组合在一起使用的新方式。
XML 简介
XML 可扩展标记语言。
XML 被设计用来传输和存储数据。
XML 和 HTML 类似,不同的是 HTML 中都是预定义标签,而 XML 中没有预定义标签, 全都是自定义标签,用来表示一些数据。
比如说我有一个学生数据: name = “孙悟空” ; age = 18 ; gender = “男” ;
用 XML 表示
<student>
<name>孙悟空</name>
<age>18</age>
<gender>男</gender>
</student>
现在已经被 JSON 取代了
{"name":"孙悟空","age":18,"gender":"男"}
HTTP
HTTP(hypertext transport protocol)协议『超文本传输协议』,协议详细规定了浏览器和万维网服务器之间互相通信的规则。
约定, 规则
请求报文
重点是格式与参数
行 POST /s?ie=utf-8 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>
- 404 找不到
- 403 无权限
- 401 未授权
- 500 内部错误
- 200 OK
express的基本使用
//1.引入express
const { request, response } = require('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 端口监听中...."); //提示语言
})
请求状态
xhr.readyState 可以用来查看请求当前的状态
https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/readyState
0: 表示 XMLHttpRequest 实例已经生成,但是 open()方法还没有被调用。
1: 表示 send()方法还没有被调用,仍然可以使用 setRequestHeader(),设定 HTTP请求的头信息。
2: 表示 send()方法已经执行,并且头信息和状态码已经收到。
3: 表示正在接收服务器传来的 body 部分的数据。
4: 表示服务器数据已经完全接收,或者本次接收已经失败了
AJAX请求
响应请求
//1.引入express
const { request, response } = require('express');
const express = require('express');
//2. 创建应用对象
const app = express();
//3. 创建路由规则
//request 是对请求报文的封装
//response 是对响应报文的封装
app.get('/server',(request,response)=>{
//设置响应头
response.setHeader('Access-Contro-Allow-Origin','*'); //允许跨域
//设置响应
response.send('Hello Express');
});
//4.监听端口启动服务
app.listen(8000,()=>{
console.log("服务已经启动,8000 端口监听中...."); //提示语言
})
发送GET请求
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>Document</title>
<style>
#result {
width: 200px;
height: 100px;
border: 1px solid #90b;
}
</style>
</head>
<body>
<button>点击发送请求</button>
<div id="result"></div>
<script>
const btn = document.getElementsByTagName("button")[0];
btn.onclick = function(){
//1. 创建对象
const xhr = new XMLHttpRequest();
const result = document.getElementById('result');
//2. 初始化 设置请求方法和url
// xhr.open('GET','http://127.0.0.1:8000/server'); //参数一:请求类型,参数二:给谁发
// 设置请求参数
// 用问号分隔加上参数的名字和值,如果有多个参数,则用&分隔
xhr.open('GET','http://127.0.0.1:8000/server?a=100&b=200&c=300');
//3. 发送
xhr.send();
//4. 事件绑定 处理服务端返回的结果
// on when 当...时候
// readystate 是 xhr 对象中的属性,表示状态
// 0(未初始化) 1(open执行完毕) 2(send执行完毕) 3(返回部分结果) 4(所有结果全部返回)
// change 发生改变
xhr.onreadystatechange = function(){ //readystate有五个值,那么这个事件会触发四次
//处理服务端返回的结果,但是需要注意,只有当状态为4的时候才能收到所有结果
//判断 (表名服务端返回了所有的结果)
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); //响应体
/*
200
GET.html:51 OK
GET.html:52 content-length: 13
content-type: text/html; charset=utf-8
GET.html:53 Hello Express
*/
//设置 result 的文本
result.innerHTML = xhr.response; //显示响应体
}
}
}
}
</script>
</body>
</html>
设置请求参数
// 用问号分隔加上参数的名字和值,如果有多个参数,则用&分隔
xhr.open('GET','http://127.0.0.1:8000/server?a=100&b=200&c=300');
可以在payload查看
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oyQGKRac-1656863397329)(ajax.assets/image-20220702155355323.png)]
发送POST请求
<!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>
//当鼠标放到div的时候向服务端发送请求
const result = document.getElementById("result");
//当鼠标移动到div时触发
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>
</body>
</html>
设置请求体
//3.发送
//请求体的格式非常灵活,前提是服务端要有与之对应的处理方式
xhr.send('a=100&b=200&c=300'); //方式一
xhr.send('a:100&b:200&c:300'); //方式二
xhr.send('1234567'); //方式三
.....
设置请求头信息
在步骤2和步骤3中添加 xhr.setRequestHeader
//设置请求头
//参数一:头的名字,参数二:头的值
// Content-Type:用来设置请求体内容的类型
// application/x-www-form-urlencoded:是参数查询字符串的类型(固定写法)
// xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded'); //预定义
xhr.setRequestHeader('name','zhangzh'); //自定义类型
这里会报错,因为服务端没办法接收,所以要修改一下服务端
//可以接受任意类型的请求
app.all('/server',(request,response)=>{
//设置响应头
response.setHeader('Access-Control-Allow-Origin','*'); //允许跨域
//特殊响应头(自定义类型)xhr.setRequestHeader('name','zhangzh'); //自定义类型
response.setHeader('Access-Control-Allow-Headers','*'); // * 表示所有类型的头信息都可以接受
//设置响应体
response.send('Hello AJAX POST');
});
服务端响应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>Document</title>
<style>
#result {
width: 200px;
height: 100px;
border: 1px solid skyblue;
}
</style>
</head>
<body>
<div id="result"></div>
<script>
const result = document.getElementById("result");
//当键盘按下,则向服务端发送请求
//绑定键盘按下事件
window.onkeydown = function(){
//1.发送请求
const xhr = new XMLHttpRequest();
//设置响应体数据的类型
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){
// result.innerHTML = xhr.response;
//手动对数据转换
// var data = JSON.parse(xhr.response);
// result.innerHTML = data.name;
//自动转换,需要设置响应体数据的类型为JSON--27行
console.log(xhr.response); //自动将字符串转换为对象
result.innerHTML = xhr.response.name;
}
}
}
}
</script>
</body>
</html>
服务端
//可以接受任意类型的请求,JSON-Server
app.all('/json-server',(request,response)=>{
//设置响应头
response.setHeader('Access-Control-Allow-Origin','*'); //允许跨域
//特殊响应头(自定义类型)xhr.setRequestHeader('name','zhangzh'); //自定义类型
response.setHeader('Access-Control-Allow-Headers','*'); // * 表示所有类型的头信息都可以接受
//响应一个数据
const data = {
name: 'zhangzh'
};
//对对象进行一个字符串的转换
let str = JSON.stringify(data);
//设置响应体
response.send(str); //这里不能直接传data,只能将data转成字符串
});
自动刷新服务插件nodemon
基于nodejs安装
安装代码
npm install -g nodemon
启动nodemon
npx nodemon 路径
IE缓存问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IE缓存问题</title>
<style>
#result{
width:200px;
height:100px;
border:solid 1px #258;
}
</style>
</head>
<body>
<button>点击发送请求</button>
<div id="result"></div>
<script>
const btn = document.getElementsByTagName('button')[0];
const result = document.querySelector('#result');
btn.addEventListener('click', function(){
const xhr = new XMLHttpRequest();
xhr.open("GET",'http://127.0.0.1:8000/ie?t='+Date.now());
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status< 300){
result.innerHTML = xhr.response;
}
}
}
})
</script>
</body>
</html>
请求超时与网络异常处理
设置网络异常
Network->Online->选offline(离线),就可以测试网络异常功能
<!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>
#result {
width: 200px;
height: 100px;
border: 1px solid skyblue;
}
</style>
</head>
<body>
<button>点击发送请求</button>
<div id="result"></div>
<script>
var btn = document.getElementsByTagName("button")[0];
var result = document.getElementById("result");
btn.onclick = function(){
const xhr = new XMLHttpRequest();
//超时设置
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;
}
}
}
};
</script>
</body>
</html>
服务端
//延时响应
app.get('/delay',(request,response)=>{
//设置响应头
response.setHeader('Access-Control-Allow-Origin','*'); //允许跨域
setInterval(()=>{
//设置响应体
response.send('延时响应');
},3000);
});
取消请求
abort函数可以将发送的请求取消掉
<!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>
</head>
<body>
<button>点击发送</button>
<button>点击取消</button>
<script>
var btns = document.getElementsByTagName("button");
var xhr = null; //因为取消请求也需要这个,所以就声明在这里
//发送请求
btns[0].onclick = function(){
xhr.open('GET','http://127.0.0.1:8000/delay');
xhr.send();
xhr.onreadystatechange = function(){
xhr = new XMLHttpRequest();
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status<300){
console.log(xhr.response);
}
}
}
}
//取消请求
btns[1].onclick = function(){
xhr.abort();
}
</script>
</body>
</html>
重复发送请求问题
如果一个用户疯狂发送同一个请求的话,会使服务器工作负荷加大
所以需要取消上一个没有发送完成的同类型的请求
axios发送请求
<!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>
<!-- 导入axios -->
<script crossorigin="anonymous" src="https://cdn.bootcdn.net/ajax/libs/axios/1.0.0-alpha.1/axios.js"></script>
</head>
<body>
<button>GET</button>
<button>POST</button>
<button>AJAX</button>
<script>
var btns = document.getElementsByTagName("button");
//配置baseURL
axios.defaults.baseURL = 'http://127.0.0.1:8000';
//GET请求
btns[0].onclick = function(){
//参数一:给谁发,参数二:发什么
// axios.get('http://127.0.0.1:8000/axios-server',{
axios.get('/axios-server',{ //加上19行的代码可以省略前面的
//url参数
params:{
id:100,
vip:7
},
// //请求头信息
headers: {
name: 'zhangzh',
age: 20
}
}).then(value => { //成功的回调函数
console.log(value);
})
}
//POST请求
btns[1].onclick = function(){
//参数一:URL,参数二:请求体,参数三:其他参数
axios.post('/axios-server',{
//请求体
data:{
username: 'admin',
password: 'admin'
}
},
//其他参数
{
//url
params: {
id:200,
vip:8
},
//请求头参数
headers: {
height:180,
weight:180
}
})
}
//通用方式发送请求
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);
//响应状态码
console.log(response.status);
//响应状态字符串
console.log(response.statusText);
//响应头信息
console.log(response.headers);
//响应体
console.log(response.data);
})
}
</script>
</body>
</html>
服务端
//axios服务
app.all('/axios-server',(request,response)=>{
//设置响应头
response.setHeader('Access-Control-Allow-Origin','*'); //允许跨域
//特殊响应头(自定义类型)
response.setHeader('Access-Control-Allow-Headers','*'); // * 表示所有类型的头信息都可以接受
const data = {name:'zhangzh'};
response.send(JSON.stringify(data));
});
使用fetch方法发送请求
位于WorkerOrGlobalScope这一个mixin中的fetch()方法用于发起获取资源的请求。它返回一个promise,这个 promise 会在请求响应后被resolve,并传回Response对象。
window和 workerGlobalScope都实现了WorkerOrGlobalScope。——这意味着基本在任何场景下只要你想获取资源,都可以使用位于WorkerOrGlobalScope 中的 fetch()方法。
当遇到网络错误时,fetch()返回的promise 会被 reject,并传回TypeError,虽然这也可能因为权限或其它问题导致。成功的 fetch()检查不仅要包括promise被resolve,还要包括
Response.ok属性为true。HTTP 404 状态并不被认为是网络错误。
fetch()方法由Content Security Policy的 connect-src指令控制,而不是它请求的资源。
注意:fetch()方法的参数与Request()构造器是一样的。
<!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>
</head>
<body>
<button>fatch请求</button>
<script>
const btn = document.getElementsByTagName("button")[0];
btn.onclick = function(){
fetch('http://127.0.0.1:8000/fatch-server?vid=10',{
//请求方法
method: 'POST',
//请求头
headers: {
name: 'zhangzh'
},
//请求体
body: 'username:admin&password:admin'
}).then(response => {
//因为不能直接拿到响应体,所以需要转换
// return response.text();
//如果服务端返回的是JSON类型,那么将上面的方法换成json的方法
return response.json();
}).then(response => {
console.log(response); //{"name":"zhangzh"} response.text()
console.log(response); // {name: 'zhangzh'} response.json()
})
}
</script>
</body>
</html>
服务端
//fatch服务
app.all('/fatch-server',(request,response)=>{
//设置响应头
response.setHeader('Access-Control-Allow-Origin','*'); //允许跨域
//特殊响应头(自定义类型)
response.setHeader('Access-Control-Allow-Headers','*'); // * 表示所有类型的头信息都可以接受
const data = {name:'zhangzh'};
response.send(JSON.stringify(data));
});
跨域
同源策略
同源策略(Same-Origin Policy)最早由 Netscape 公司提出,是浏览器的一种安全策略。
同源: 协议、域名、端口号 必须完全相同。
违背同源策略就是跨域。
AJAX默认为遵循的是同源策略
<!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>
</head>
<body>
<!-- 页面在服务端启动(127.0.0.1::9000/home),不要在这里启动,不然就是跨域 -->
<h1>张志航</h1>
<button>点击获取数据</button>
<script>
const btn = document.getElementsByTagName("button")[0];
btn.onclick = function(){
const x = new XMLHttpRequest();
//这里因为是满足同源策略,所以url可以简写
//页面、数据都是同一个服务器(9000端口)来的
x.open('GET','/data');
x.send();
x.onreadystatechange = function(){
if(x.readyState === 4){
if(x.status >= 200 && x.status<300){
console.log(x.response);
}
}
}
}
</script>
</body>
</html>
服务端
const experss = require('express');
const app = experss();
app.get('/home',(request,response) => {
//响应一个页面,启动页面
response.sendFile(__dirname + '/index.html');
})
app.get('/data',(requset,response)=>{
response.send("用户数据");
});
app.listen(9000,()=>{
console.log("服务已经启动...");
})
document.getElementsByTagName(“button”)[0];
btn.onclick = function(){
const x = new XMLHttpRequest();
//这里因为是满足同源策略,所以url可以简写
//页面、数据都是同一个服务器(9000端口)来的
x.open('GET','/data');
x.send();
x.onreadystatechange = function(){
if(x.readyState === 4){
if(x.status >= 200 && x.status<300){
console.log(x.response);
}
}
}
}
</script>
```
服务端
const experss = require('express');
const app = experss();
app.get('/home',(request,response) => {
//响应一个页面,启动页面
response.sendFile(__dirname + '/index.html');
})
app.get('/data',(requset,response)=>{
response.send("用户数据");
});
app.listen(9000,()=>{
console.log("服务已经启动...");
})