文章目录
一、原生AJAX
(一) 简介
- AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)
- 最大优势:可以在网页不刷新的情况下向服务器发送http请求,得到http响应。
(二)XML
- XML
- 可扩展标记语言(eXtensible Markup Language)。
- 用途:存储和传输数据
- 标签:没有预定义标签
- HTML
- 超文本标记语言
- 用途:在网页中呈现数据
- 标签:都是预定义标签
- 相同点:由标签组成
- 过去ajax使用过xml格式进行数据传输,现在用json格式
(三)AJAX的特点
- 优点
(1)可以无需刷新页面与服务器端进行通信。
(2)允许根据用户时间更新部分页面内容。 - 缺点
(1)没有浏览历史,没有后退可言。
(2)存在跨域问题。
(3)SEO(指按照搜索引擎的算法,提升你的文章在搜索引擎中的自然排名)不太友好。
(四)HTTP协议
- HTTP:HyperText Transfer Protocol 超文本传输协议
- 请求报文的构成
- 请求行:请求方法(GET/POST等)、URL、协议版本;之间由空格分隔
- 请求头部:为请求报文添加了一些附加信息,由“名/值”对组成
- Host:IP+端口号 或 域名
- Cookie:name=guigu
- Content-Type:定义网络文件的类型和网页的编码,决定浏览器将以什么形式、什么编码读取这个文件
- User-Agent:发送请求的应用程序名称 (例:chrome 83)
- 请求空行:固定的,必须有
- 请求体:使用时通常get请求不带body,post请求带body
【补充】
(1) get请求能否带请求体存在争议:get请求能携带请求体吗?
(2) 报文格式:菜鸟教程-消息结构
- 响应报文的构成
- 响应行:协议版本、响应状态码、响应状态字符串
- 响应头
- Content-Type:响应正文的类型
- Content-Length:响应正文长度
- Content-Encoding:响应正文使用的数据压缩格式
- 空行
- 响应体
<html>
<head></head>
<body>
<h1>Hello,World!";</h1>
</body>
</html>
//请求页面时,返回html代码,浏览器引擎做解析和渲染
参考CSDN文档:HTTP请求和响应报文格式
(五)浏览器中查看报文
1. 操作
F12开发者工具–>选定Network–>选定一条请求
2. 请求与响应数据
(1)get请求
- Headers:包含行和头的内容(点击View source可以查看请求/响应行)
- Response Headers:响应头
- Request Headers:请求头
- Response:响应体
- Preview:对响应体内容解析之后的预览
- PayLoad:Query String Parameters
请求参数的格式化显示(键值对格式,检测参数是否发送的有利工具)
(2)post请求
Headers增加了一个Form Data:查看post请求的请求体,其余与get请求相同
(六)实操环境
- 安装 node.js
(1)node.js:an open-source, cross-platform JavaScript runtime environment. (一个开放的、跨平台的js运行时环境)
(2)点击下载安装
(3)检验:cmd命令行窗口输入 node -v,如果输出版本号证明安装成功 - 使用express框架
(1)express:基于 Node.js 平台的Web 开发框架。点击跳转中文官网
(2)用管理员权限打开vscode,工作空间根目录打开terminal终端窗口,运行语句:npm init --yes
(npm-init:create a package.json file)
(3)创建express.js文件,按照官网指南,使用express框架:
//1.引入express
const express = require("express");
//2.创建应用对象
const app = express();
// 3.创建路由规则
/*
get函数的两个参数:路径;回调函数
request是对请求报文的封装
response是对响应报文的封装
*/
app.get('',(request,response) =>{
//设置响应
response.send('HELLO EXPRESS');
})
//4.监听端口
app.listen(8000,() => {
console.log("服务器已启动,8000端口监听中......")
})
命令行启动指令:node express.js
(七)XMLHttpRequest 对象
1. 是什么
- XHR objects are used to interact with servers,can be used to retrieve any type of data, not just XML. (用于与服务器交换数据,任何类型数据,不仅仅是XML)
2. 在哪里
- 所有现代浏览器(IE7+、Firefox、Chrome、Safari 和 Opera)都有内建的 XMLHttpRequest 对象。(IE5 和 IE6 使用 ActiveXObject)。
3. 构造函数
- 创建 XMLHttpRequest 对象:new XMLHttpRequest();
- 创建 ActiveXObject对象:new ActiveXObject(“Microsoft.XMLHTTP”);
解决浏览器的兼容问题:
var xmlhttp;
//检查浏览器是否支持 XMLHttpRequest 对象
if (window.XMLHttpRequest)
{
// IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
xmlhttp=new XMLHttpRequest();
}
else
{
// IE6, IE5 浏览器执行代码
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
4. 常用方法
(1)open()
【作用】初始化请求
【语法】open(method,url[, async, user, password])
【参数】
- method(String):http请求方法,GET、POST等 全部请求方法:菜鸟教程
- url(String):发送请求的路径字符串,请求访问的地址
- async(Boolean):配置是否为异步执行,默认值为true
- username:如果服务器需要验证,该参数指定用户名,如果未指定,当服务器需要验证时,会弹出验证窗口。
- password:验证信息中的密码部分,如果用户名为空,则该值将被忽略。
(2)send()
【作用】实际发出http请求
【语法】send([body])
【参数】
- 空参:表示 HTTP 请求只有一个 URL,没有数据体,典型例子就是 GET 请求
- body:XHR请求中发送的请求体数据,可以是
- 序列化的文档
- XMLHttpRequestBodyInit:根据 Fetch 规范,它可以是 Blob、ArrayBuffer、TypedArray、DataView、FormData、URLSearchParams 或字符串文本或对象。
【补充】
URL编码:规定了一些具有特殊意义的字符,常被用来分隔两个不同的 URL 组件,被称为保留字符。例如:
- 冒号:用于分隔协议和主机组件;
- 斜杠:用于分隔主机和路径;
- ?:用于分隔路径和查询参数等。
- =:用于表示查询参数中的键值对。
- &:用于分隔查询多个键值对。
(3)abort()
【作用】中断一个已经发送的请求,属性值readyState和status都变成0
(4)setRequestHeader()
【作用】给http头部的一个请求头设置值,必须在open()之后send()之前调用。如果同一个请求头调用这个方法多次,那么会自动合并成一个请求头。
【语法】setRequestHeader(header, value)
【参数】
- header:要设置其值的请求头的名称。
- value:要设置的值
(5)getResponseHeader()
【作用】返回包含指定响应头的文本的字符串。
【语法】getResponseHeader(headerName)
【参数】
- headerName:要返回的报文项名称
(6)getAllResponseHeaders()
【作用】返回所有的响应头,是以 CRL(回车符和换行符)分割的字符串 如果没有收到任何响应,返回null。
5. 对象常用属性
(1)readyState
【解释】xhr对象的属性,存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。
【取值】
- 0 ==> 未初始化,是该属性的初始值
- 1 ==> open方法已经调用完毕,服务器连接已建立
- 2 ==> send方法已经调用完毕,服务器已经接受请求
- 3 ==> 请求处理中
- 4 ==> 服务器返回了所有结果,请求已完成
(2)respnseType
【解释】一个枚举的字符串值,指定响应中包含的数据类型。
(3)response
【解释】返回响应体内容
【取值】取到的结果会根据 this.responseType 的变化而变
(4)respneseText
【解释】返回在发送请求后从服务器接收的文本。
【取值】永远获取的是字符串形式的响应体(是区别于respnse的地方)
(5)status
【解释】返回 XMLHttpRequest 响应的数字 HTTP 状态代码。
【取值】
- 1xx - 信息提示
- 2xx - 成功(xhr.status >= 200 && xhr.status < 300 可以判定请求成功)
- 3xx - 重定向
- 3xx - 重定向
- 5xx - 服务器错误
(6)statusText
【解释】返回HTTP 服务器响应状态的字符串消息。
6. 事件
(1)readystatechange
readyState值变化时触发,在一次请求过程中,readyState共有5个值,所以onreadystatechange事件触发4次。
(2)timeout
当进度由于预设时间到期而终止时,将触发超时事件。
(3)load
当 XMLHttpRequest 事务成功完成时,将触发 load 事件。
(4)loadend
在请求完成时触发,无论是成功(load后)还是失败
(5)error
请求遇到错误时触发
7. demo:post请求
(1). 在html中定义一个div,鼠标移入时发送post请求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>AJAX发送post请求</title>
<script type="text/typescript"></script>
<style>
#result{
width: 200px;
height: 200px;
border: 2px solid #a11;
}
</style>
</head>
<!-- 需求:鼠标移入div区域时发送post请求,服务器响应结果显示在div中 -->
<body>
<div id = "result"></div>
</body>
<script>
//获取div元素
const ele = document.getElementById("result");
//绑定鼠标移入事件
ele.onmouseover = function(){
//创建http对象
const xhr = new XMLHttpRequest();
//初始化请求
xhr.open("POST","http:127.0.0.1:8000/server");
//设置请求头
xhr.setRequestHeader('name','miqi');
//发送post请求
xhr.send("name=wang&pwd=123");
//监听状态变化获取结果
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
ele.innerHTML = xhr.response;
}
}
}
}
</script>
</html>
(2). 创建server.js文件,补充post方法
//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');
});
app.post('/server',(request,response) => {
//设置响应头:允许跨域
response.setHeader('Access-Control-Allow-Origin','*');
//设置响应体
response.send('HELLO EXPRESS POST');
});
//4.监听端口
app.listen(8000,() => {
console.log("服务器已启动,8000端口监听中......")
})
(3). 自定义请求头
//----------------------自定义内容--------------------
xhr.setRequestHeader('name','miqi');
//-----------------------配置内容--------------------
//post --> all:可以接收任意类型的请求
app.all('/server',(request,response) => {
//设置响应头:允许跨域
response.setHeader('Access-Control-Allow-Origin','*');
//【新增内容】设置响应头:可以接受所有类型的头信息
response.setHeader('Access-Control-Allow-Headers','*');
//设置响应体
response.send('HELLO EXPRESS POST');
});
(八)响应JSON格式数据
- json转字符串:JSON.stringify(data);
const data = {
name:'miqi'
}
let str = JSON.stringify(data);
- 解析
(1)手动解析:JSON.parse(data)
let data = JSON.parse(xhr.response);
var name = data.name; //name得到的结果:miqi
(2)自动解析:设置响应体数据的类型
xhr.responseType = 'json';
var name = xhr.response.name; //name得到的结果:miqi
(九)nodemon工具
- 用途:自动检测文件变化,自动重新启动程序
- 安装:npm install -g nodemon 点击访问官网
- 启动指令变为:npx nodemon server.js
(十) 常见问题
1. AJAX中的ie缓存问题
- 解决:xhr.open(‘GET’,‘http://127.0.0.1:8000/ie?t=’+Date.now());
- 原理:给请求加时间戳,浏览器会默认这是多次不同的请求,就不会再读取缓存而是重新请求。
2. 网络请求超时及异常处理
//超时时长设置 2s 设置
xhr.timeout = 2000;
//超时事件:超时之后请求会自动取消
xhr.ontimeout = function(){
alert("网络异常,请稍后重试!");
}
//网络异常事件:断网
xhr.onerror = function(){
alert("你的网络似乎出了一些问题!");
}
3. AJAX重复发送请求
- 问题:当客户端反复向服务器发送一个相同的请求时,服务器需要频繁处理,压力会很大。
- 解决思路:发一个新的请求时,先判断之前有没有相同请求,如果有,可以把先前的请求取消,只发送最新的请求。
- demo:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>重复请求问题</title>
<style>
#result{
width: 200px;
height: 200px;
border: 2px solid #3aa;
}
</style>
</head>
<body>
<button>点击发送请求</button>
</body>
<script>
//获取元素
const btn = document.getElementsByTagName("button")[0];
//标识变量:是否正在发送AJAX请求
let isSending = false;
let xhr = null;
//绑定事件
btn.addEventListener('click',function(){
//判断标识变量
if(isSending === true){
//如果正在发送,则取消该请求,创建一个新的请求
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;
}
}
})
</script>
</html>
二、jQuery中的AJAX
1. get方法
- 语法结构:$.get(url [.data] [.callback] [.type])
- 参数:
- url ==> String 请求的HTML页的url地址
- data ==> Object 请求参数,发送至服务器的key/value数据
- callback ==> Function 载入成功时回调函数
- type ==> String 服务器返回内容的格式
//例子:
$.get('http://127.0.0.1:8000/jquery-server',{a:100,b:200},function(data){
console.log(data);
},'json')
2. post方法
- 语法结构与$.get()方法相同。
//例子:
$.post('http://127.0.0.1:8000/jquery-server',{a:100,b:200},function(data){
console.log(data);
},'json')
3. ajax方法
- 语法结构:$.ajax({name:value, name:value, … })
- 参数:一个对象
//例子:
$.ajax({
//url
url:'http://127.0.0.1:8000/jquery-server',
//参数
data:{a:100,b:200},
//请求的类型
type:'GET',
//响应体结果类型
dataType:'json',
//成功的回调
success:funtion(data){
console.log(data);
},
//失败的回调
error:function(){
console.log("出错了!");
},
//超时时间
timeout:2000,
//头信息:自定义请求头
headers:{
c:300,
d:400
}
})
更多的属性参考:jQuery中文网-AJAX
三、axios
目前前端最热门的ajax工具库,vue和react推荐的ajax工具包
【补充】 BootCDN:国内前端开源项目网址
1. axios.get(url[, config])
- url:请求地址
- config:url参数配置对象{}(即请求时拼接到url末尾的请求参数)
2. axios.post(url[, data[, config]])
- data:请求体,会以json字符串的格式发送到服务器
【注意】
在axios的post方法中,请求体参数在url配置参数之前
3. axios的通用方式发送请求
(1) axios(config)
(2) axios(url[,config])
4. 示例
(1)引入在线的axios包。页面放三个按钮,点击之后分别向服务器发送get、post、通用请求。
<script src="https://cdn.bootcdn.net/ajax/libs/axios/1.3.4/axios.js"></script>
<body>
<button>GET</button>
<button>POST</button>
<button>AJAX</button>
</body>
(2)设置baseURL的值。(baseURL:程序会将其附加到 url 前面,除非 url 是绝对路径。)
//获取按钮元素,为后面绑定事件发请求做准备
const btns = document.getElementsByTagName("button");
//设置baseURL
axios.defaults.baseURL = 'http:127.0.0.1:8000';
(3)第一个按钮绑定点击事件,调用axios.get(url[, config]) 函数发送axios的get请求,注意不能带请求体。
btns[0].onclick = function(){
axios.get('/axios-server',{
//url参数
params:{
id:100,
vip:7
},
//请求头信息
headers:{
name:'miqi',
age:26
}
}).then(value => {
console.log(value);
})
}
(4)第二个按钮绑定点击事件,调用axios.post(url[, data[, config]])方法,想服务器发送post请求。
btns[1].onclick = function(){
axios.post('/axios-server', //url
{//请求体
username:"admin",
password:"123"
},
{//配置参数
//url参数
params:{
id:200,
vip:9
},
//请求头参数
headers:{
height:180,
weight:150
},
})
}
(5)按钮3绑定点击事件,调用axios(params)函数,发送通用请求,可以通过method配置指定请求方法POST/GET…
btns[2].onclick = function(){
axios({
//请求方法
method:"POST",
//请求路径
url:'/axios-server',
//url参数
params:{
vip:10,
level:30
},
//头信息参数
headers:{
a:100,
b:200
},
//请求体参数
data:{
username:"miqi",
password:'111'
}
}).then(response => {
console.log(response);
//响应状态码
console.log(response.status);
//响应状态字符串
console.log(response.statusText);
//响应头信息
console.log(response.headers);
//响应体
console.log(response.data);
})
}
【补充】url参数配置对象的其他属性请参考github–>Request Config
四、使用fetch()发送AJAX请求
- fetch():用于发起获取资源的请求,它返回一个promise。
- 语法结构:Promise fetch(input[, init]);
- 参数:
- input:定义要获取的资源,可以是:
- 一个字符串:包含URL
- 一个Request对象(处于试验阶段,所以我们用URL)
- init:一个配置项对象,包括所有对请求的设置。可选的参数有:MDN-api参考
- input:定义要获取的资源,可以是:
- 示例
//btn是页面的按钮元素,点击按钮发送请求
btn.onclick = function(){
//配置url参数:?vip=50
fetch('http://127.0.0.1:8000/fetch-server?vip=50',{
//请求方法
method:'POST',
//请求头
headers:{
name:'111'
},
//请求体:此处演示字符串,注意是body不是data
body:'username=admin&pwd=admin'
}).then(response => {
//得到响应体的文本信息
// return response.text();
//得到响应体的json对象
return response.json(response);
}).then(response => {
console.log(response);
})
}
- 项目中使用axios居多,fetch()居少。
五、跨域
(一)同源策略
1. 介绍
(1)同源策略最早由Netscape公司提出,是浏览器的一种安全策略。
(2) 同源:(网页url与AJAX请求的目标资源的url)协议、域名、端口号 必须完全相同。
(3) AJAX默认遵循同源策略。
(4) 跨域:违背同源策略就是跨域。例:
- port3000 --> port 8000 (3000端口向8000端口发请求)
- http协议 --> https协议发请求等。
2. 遵循同源策略的案例
(1)创建server.js文件
const express = require('express');
const app = express();
app.get('/home',(request,response) => {
//响应一个页面
//__dirname:当前模块的目录名,绝对路径,node.js内容
response.sendFile(__dirname + '/index.html');
});
app.get('/data',(request,response) => {
response.send("用户数据");
})
app.listen(9000,() => {
console.log("服务器已启动,9000端口监听中...");
})
(2)绑定页面按钮,发送同源请求
btn.onclick = function(){
const x = new XMLHttpRequest();
//这里满足同源策略,所以URL可以简写。浏览器会自动加上协议、域名和端口
x.open("GET",'/data');
x.send();
//绑定事件
x.onreadystatechange = function(){
if(x.readyState === 4){
if(x.status >= 200 && x.status < 300){
console.log(x.response);
}
}
}
(二)如何解决跨域
1. JSONP
(1)是什么?
- JSON with Padding
- 是一个非官方的跨域解决方案,纯粹凭借程序员的聪明才智开发出来的,只支持get请求。
(2)实现方式
- 网页中有一些标签天生具有跨域能力,如:img、link、iframe、script;
- JSONP就是利用script标签的跨域能力来发送请求的。
【注意】
script标签引用的资源/请求的结果期望是一段js代码,可以用来解析执行
(3)跨域请求数据的方式(jsonp跨域原理)
- 服务端响应体是函数调用,而函数的实参就是要给客户端返回的数据
//jsonp服务
app.all('/jsonp-server',(request,response) => {
const data = {
name:"jsonp数据的名字",
};
//将数据转为字符串
let str = JSON.stringify(data);
//返回结果
response.end(`handle(${str})`);
/*
1. end():结束响应,告诉客户端所有消息已经发送。
当所有要返回的内容发送完毕时,该函数必须被调用一次。
如果不调用该函数,客户端将永远处于等待状态。
2. 参数是es6模板字符串格式,便于返回之后拼接解析
*/
})
- 响应体中的函数调用,函数必须提前在页面中声明,通过本地浏览器访问页面,而不是http请求,以便测试跨域数据传递
<script>
//处理数据
function handle(data){
//获取result元素
const result = document.getElementById('result');
result.innerHTML = data.name;
}
</script>
<!-- 通过script标签实现跨域请求数据 -->
<script src="http://127.0.0.1:8000/jsonp-server"></script>
(4)原生的jsonp跨域练习
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<!-- 需求:页面键入用户名,失去焦点之后,向服务端发送请求,服务端响应数据:已存在,不允许使用,input框变红色 -->
<body>
用户名:
<input type="text" id="username">
<p></p>
</body>
<script>
//获取元素
const username = document.getElementById("username");
const pLabel = document.querySelector("p");
//声明handle函数
function handle(data){
username.style.border = 'red 1px solid';
//修改p标签提示文本
pLabel.innerHTML = data.msg;
}
//绑定失去焦点事件
username.onblur = function(data){
//获取用户输入的值
let username = this.value;
//向服务器端发请求,检测用户是否存在
//1. 创建script标签
const script = document.createElement('script');
//2. 设置标签的src属性
script.src = 'http://127.0.0.1:9000/exer1';
//3.将script插入到文档中
document.body.append(script);
}
</script>
</html>
(5)jQuery的jsonp跨域练习
- 客户端请求代码
url请求地址中加 ?callback=? 的固定参数,浏览器会将其值自动补全
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>jQuery-jsonp练习</title>
</head>
<style>
#result{
width: 200px;
height: 200px;
border: 2px solid #333;
}
</style>
<!-- bootcdn引入在线的jQuery包 -->
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.3/jquery.js"></script>
<!-- 需求:点击按钮发请求,返回的结果显示在框中 -->
<body>
<button>点击发送请求</button>
<div id="result"></div>
</body>
<script>
$("button").eq(0).click(function(){
//jQuery.getJSON(url[,data,success(data,status,xhr)])
//通过 HTTP GET 请求载入 JSON 数据。
$.getJSON('http://127.0.0.1:9000/exer2?callback=?',function(data){
$("#result").html(`
名称:${data.name},
年龄:${data.age}
`)
})
})
</script>
</html>
- 服务端响应代码
不同于原生的 jsonp 请求,jQuery的请求头中包含一个callback参数,通过request.query.callback 获取其值,其值作为原生jsonp的end()第一个参数即被调函数。
//练习2 服务:jQuery jsonp请求
app.all("/exer2",(request,response) => {
data={
name:'miqi',
age:26
};
data = JSON.stringify(data);
//接收callback参数
//request.query对象包含了本次请求的QueryString的键值对列表
let cb = request.query.callback;
response.end(`${cb}(${data})`)
});
2. CORS
(1)是什么?
- Cross-Origin Resource Sharing:跨域资源共享
- 官方的跨域解决方案
- 特点:不需要在客户端做任何特殊操作,完全在服务器端进行处理,支持get、post
- 跨域资源共享标准新增了HTTP首部字段,允许服务器声明哪些源站通过浏览器有权访问哪些资源
(2)实现方式
- 通过设置响应头告诉浏览器,该请求允许跨域,浏览器收到该响应以后就会对响应放行。
(3)关键:只需要设置响应头
app.all('/cors',(request,response) => {
//设置响应头
response.setHeader('Access-Control-Allow-Origin',"*");
response.send("Hello,CORS");
})