参考课程:
参考:【尚硅谷】3小时Ajax入门到精通_哔哩哔哩_bilibili
参考:Ajax - Web 开发者指南 | MDN
根据MDN文档的描述,大致可以这样描述AJAX:
Asynchronous JavaScript + XML(异步JavaScript和XML), 其本身不是一种新技术,而是一个在 2005年被Jesse James Garrett提出的新术语,用来描述一种使用现有技术集合的‘新’方法,包括: HTML 或 XHTML, CSS, JavaScript, DOM, XML, XSLT, 以及最重要的
XMLHttpRequest
。当使用结合了这些技术的AJAX模型以后, 网页应用能够快速地将增量更新呈现在用户界面上,而不需要重载(刷新)整个页面。这使得程序能够更快地回应用户的操作。简单点说,就是使用 XMLHttpRequest 对象与服务器通信。 它可以使用JSON,XML,HTML和text文本等格式发送和接收数据。AJAX最吸引人的就是它的“异步”特性,也就是说他可以在不重新刷新页面的情况下与服务器通信,交换数据,或更新页面。
上面提到了异步,那么首先我们就要搞清楚“异步”的概念:(以下解释来自百度百科相关词条)
异步:一种通讯方式,对设备需求简单。我们的PC机提供的标准通信接口都是异步的。
异步双方不需要共同的时钟,也就是接收方不知道发送方什么时候发送,所以在发送的信息中就要有提示接收方开始接收的信息,如开始位,同时在结束时有停止位。
异步的另外一种含义是计算机多线程的异步处理。与同步处理相对,异步处理不用阻塞当前线程来等待处理完成,而是允许后续操作,直至其它线程将处理完成,并回调通知此线程。
但此处需要明确的是:异步与多线程与并行不是同一个概念.
与“异步”相对的有一个“同步”的概念,我把“同步”简单地理解为:某项事务必须等另一项事务操作完成后并返回某个结果后(这个结果的类型可能是“成功”也可能是“失败”)才能继续往下进行。而异步就恰好相反,我不需要非得等某个事务完成并给我返回结果后才能进行后续操作。
那么在AJAX中,“异步”就体现在客户端向服务端发送了某个请求之后,不会导致与该请求的相关代码之后的代码无法执行,而是继续往下走,而至于服务端则在此时对相应的请求进行处理,无论处理结果是成功的还是失败的,都会给客户端一个响应。
也可以参看以下博客和讨论(当然网络上还有很多相关的描述和讨论,但是大抵上都差不多,可以多参阅几篇相关的文章再形成自己的理解,至于对于相关底层的研究这里就不再展开了):
更新:2021年8月19日22:29:58
参考:怎样理解阻塞非阻塞与同步异步的区别? - 知乎
参考:同步(Synchronous)和异步(Asynchronous) - myCpC - 博客园
参考:真的理解同步和异步了吗?_Small_White-CSDN博客
可以无需刷新页面而与服务器端进行通信。
允许你根据用户事件来更新部分页面内容。
维基百科的相关解释如下:
超文本传输协议(英语:HyperText Transfer Protocol,缩写:HTTP)是一种用于分布式、协作式和超媒体信息系统的应用层协议[1]。HTTP是万维网的数据通信的基础。通过HTTP或者HTTPS协议请求的资源由统一资源标识符(Uniform Resource Identifiers,URI)来标识。
HTTP是一个客户端(用户)和服务端(网站)之间请求和应答的标准,通常使用TCP协议。通过使用网页浏览器、网络爬虫或者其它的工具,客户端发起一个HTTP请求到服务器上指定端口(默认端口为80)。我们称这个客户端为用户代理程序(user agent)。应答的服务器上存储着一些资源,比如HTML文件和图像。我们称这个应答服务器为源服务器(origin server)。在用户代理和源服务器中间可能存在多个“中间层”,比如代理服务器、网关或者隧道(tunnel)。
通常,由HTTP客户端发起一个请求,创建一个到服务器指定端口(默认是80端口)的TCP连接。HTTP服务器则在那个端口监听客户端的请求。一旦收到请求,服务器会向客户端返回一个状态,比如"HTTP/1.1 200 OK",以及返回的内容,如请求的文件、错误消息、或者其它信息。
HTTP协议报文主要分为请求报文和响应报文两种。关键是注意格式与参数。两者都由【行】、【头】、【空行】、【体】几部分组成。
行 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
行 HTTP/1.1 200 OK
头 Content-Type: text/html;charset=utf-8
Content-length: 2048
Content-encoding: gzip
空行
体
尚硅谷
更多具体细节可以直接观看视频:
参考:【尚硅谷】3小时Ajax入门到精通-06.尚硅谷_AJAX-Chrome网络控制台查看通信报文_哔哩哔哩_bilibili
F12 或 Ctrl + Shift + I 打开谷歌开发者工具,切换到【Network】选项卡。
官网描述:Express 是一个保持最小规模的灵活的 Node.js Web 应用程序开发框架,为 Web 和移动应用程序提供一组强大的功能。
简单来说就是Express可以为你提供一个模拟的小型服务器环境以便于我们后续的学习。这个框架是基于Node.js
平台的,所以要先安装Node.js
。
参考:Express - 基于 Node.js 平台的 web 应用开发框架 - Express 中文文档 | Express 中文网
Node.js
:到官网 下载 | Node.js中文网 按提示下载即可。CMD
,输入node -v
,回车后有相应版本信息输出即可。Express
框架:npm install express
。如果速度较慢,可以自行配置淘宝镜像或使用yarn
,详情可自行搜索相关内容。//server.js
//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 端口监听中....");
});
上面的server.js
的内容会根据学习的内容变化而做相应的更新,但是这一块不是我们学习AJAX的重点,所以不过多讲述。
当我们修改服务端相关代码后,需要用node xxxx.js
(xxxx.js
是存储你的服务端代码的JS文件,比如:server.js
)重启当前服务。这样手动重启难免有点麻烦,可以通过npm
安装nodemon
这个工具来实现当我们修改代码后,服务器就自动重启的效果。可在当前目录下执行以下命令:
npm install nodemon
此后只要在第一次启动服务器时用nodemon xxxx.js
运行服务端代码即可,之后修改服务端代码后就该工具就将自动帮我们重启服务。
//1. 创建XMLHttpRequest类型的对象
const xhr = new XMLHttpRequest();
//2. 调用open方法对请求进行初始化 设置请求方法为“GET”且指明目标资源地址,可以带上查询字符串
xhr.open('GET', 'http://127.0.0.1:8000/server?a=100&b=200&c=300');
//3. 调用send方法发送请求
xhr.send();
//4. 给xhr对象绑定相关响应事件以处理服务端返回的结果
// on when 当....时候
// readystate 是 xhr 对象中的属性, 表示状态 0 1 2 3 4(详见下方解释)
// change 改变
xhr.onreadystatechange = function() {
//判断 (服务端返回了所有的结果)
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);//响应体
} else {
console.warn('请求结果不成功');
}
}
}
onreadystatechange
响应函数给 XMLHttpRequest
类型的对象绑定这个事件回调函数。当(on)该对象的 readyState
属性被改变(change)时,该函数就会被触发。 readyState
属性代表当前的这个请求对象的状态,而 readyState
属性共有5个状态:
readyState
===0:readystate
的初始值,表示当前请求未初始化readyState
===1:表示open()
方法被调用完毕readyState
===2:表示send()
方法被调用完毕readyState
===3:表示服务端返回了部分的结果readyState
===4:表示服务端返回了全部的结果,应该在此时对服务端的相应结果做处理xhr.status
和响应状态字符串xhr.statusText
xhr.status
表示当前请求对象的响应状态码,xhr.statusText
表示相对应的响应状态字符串。具体的对应关系可看下方链接中的表格:
参考:HTTP 响应的格式及状态码_简言-CSDN博客_响应状态码
分类 | 分类描述 | 常见状态码 |
---|---|---|
1xx | 提示信息,服务器收到请求,表示目前是协议处理的中间状态, 需要请求者继续执行操作 |
|
2xx | 成功,操作被成功接收并正确处理 | 200、204、206 |
3xx | 重定向,所请求的资源发生变动,需要客户端进一步的操作 (比如重新发出请求)以完成 |
301、302、304 |
4xx | 客户端错误,请求报文有误,比如包含语法错误或无法完成请求 | 400、403、404 |
5xx | 服务器错误,服务器在处理请求的过程中发生了内部错误 | 500、501、502、503 |
常见状态码及对应描述字符串:
只要在open()
方法中的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');
注意,参数前带上?
,且多个参数之间用&
分隔。可以在开发者工具中观察结果:
只要将xhr.open()
方法中的第一个参数改为POST
即可。但是要注意要事先在服务端配置相应的路由规则。
xhr.open('POST', 'http://127.0.0.1:8000/server');
只要给xhr.send()
方法传入即可。传入的参数可以非常灵活,只要服务端能够对收到的数据进行解析即可。我们一般会传入JSON格式的字符串以方便处理。
xhr.send('a=100&b=200&c=300'); // 用得较多
// xhr.send('a:100&b:200&c:300');
// xhr.send('1233211234567');
setRequestHeader()
在xhr.open()
和 xhr.send()
方法中间添加一个 xhr.setRequestHeader()
方法即可。可以给后端服务器发送一些身份校验方面的信息。
// 设置预定义请求头,如:Content-Type
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
// 设置自定义请求头,如:name
xhr.setRequestHeader('name','atguigu');
注意:在设置自定义请求头时,可能会报错。
Access to XMLHttpRequest at 'http://127.0.0.1:8000/server' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: Request header field name is not allowed by Access-Control-Allow-Headers in preflight response.
因为没有在服务端事先进行相关设置的情况下,向服务端发送自定义的请求头是默认不被允许的。需要在服务端进行如下设置:
app.all('/server', (request, response) => {
//设置响应头,设置允许跨域,下面这行不能注释,这里只是为了突出显示第二个设置项
// response.setHeader('Access-Control-Allow-Origin', '*');
//在响应头中设置允许接受任意类型的请求头
response.setHeader('Access-Control-Allow-Headers', '*');
//设置响应体,下面这行不能注释,这里只是为了突出显示第二个设置项
// response.send('HELLO AJAX POST');
});
上面的Access-Control-Allow-Headers
设置就是让服务端接受任意类型的请求头,包括我们自定义的字段。注意,在设置了这个响应头字段后,还要处理OPTIONS
请求的问题,因为当你设置了Access-Control-Allow-Headers
这个字段后,客户端会发送一个OPTIONS
请求来做权限的校验以确定这个头信息是否可用。这时可以将app.get()
改为app.options()
,也可以把app.get()
直接改为app.all()
以匹配所有的请求。
注意,设置相关响应头信息的这些工作都是后端的范畴。
在服务端给客户端发送JSON格式的字符串:
// server.js
app.all('/json-server', (request, response) => {
……
//响应一个数据
const data = {
name: 'atguigu'
};
//对对象进行字符串转换
let str = JSON.stringify(data);
//设置响应体
response.send(str);
});
注意上面是调用了JavaScript原生的 JSON.stringify(obj)
方法来将要响应的对象类型的数据转化为了字符串类型的数据。
那么在客户端就要对返回的数据做相应的处理:
// 客户端代码
……
//设置响应体数据的类型
xhr.responseType = 'json';
//绑定响应函数
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
// 1. 手动对数据转化
// let data = JSON.parse(xhr.response);
// console.log(data);
// result.innerHTML = data.name;
// 2. 自动转换
console.log(xhr.response);
result.innerHTML = xhr.response.name;
}
}
}
因为客户端收到的数据是JSON格式的字符串,所以可以直接调用JavaScript原生的 JSON.parse(str)
函数将字符串转为原本的对象类型。
在响应函数外直接设置 xhr.responseType = 'json'
,这样相当于事先告诉客户端把来自服务端的响应体的数据格式当做是JSON。这样客户端就会自动对接收到的数据进行转换,我们可以直接把 xhr.response
看做是一个对象来用。
在IE浏览器中,如果我们多次发送AJAX请求,那么服务端返回的结果可能不是最新的结果而是IE浏览器的本地缓存数据,这对于一些对时效性有要求的场景就不友好。只需要在 xhr.open()
方法中的URL参数后追加一个时间戳参数即可。
xhr.open('GET','http://127.0.0.1:8000/ie?t='+Date.now());
现实情况中可能会出现因网络拥堵等等原因而导致的请求超时的情况。这时我们可以在客户端设置一个超时上限xhr.timeout
并设置一个超时后的响应函数 xhr.ontimeout
,当请求在这个限定的时间内没收到响应的话就会自动调用这个响应函数。
我们也可以设置一个网络异常回调:xhr.onerror
。当网络情况异常时(比如:断网),就会调用这个函数。
//超时设置(单位:ms),下面就相当于是设置了2s的超时时间
xhr.timeout = 2000;
//超时回调
xhr.ontimeout = function () {
alert("网络异常, 请稍后重试!!");
}
//网络异常回调
xhr.onerror = function () {
alert("你的网络似乎出了一些问题!");
}
当我们给服务端发送了一个AJAX请求,在响应还没回来之前,我们可以通过 xhr.abort()
方法手动取消刚才发出去的请求。
xhr = new XMLHttpRequest();
xhr.open("GET",'http://127.0.0.1:8000/delay');
xhr.send();
……
// 在请求的响应回来之前手动取消该请求
xhr.abort();
有时可能因为网络拥堵等原因,用户的操作可能会重复向服务器发送某个请求,这就可能导致服务器压力过大,尤其是在用户数量较多,并发量较大的情况下。所以我们很有必要限制用户发送的请求是不重复的。主要是通过一个自定义的标志量来判断之前是否有相同请求是在发送中的状态,如果有的话就取消前一次的请求,这样就能保证始终只有一个有效的请求在发送。
let x = null;
// 标识当前是否正在发送AJAX请求
let isSending = false;
btn.onclick = function(){
// 判断标识变量,如果正在发送, 则取消该请求, 创建一个新的请求,注意这句一定要在new语句前
if(isSending) x.abort();
x = new XMLHttpRequest();
x.open("GET",'http://127.0.0.1:8000/delay');
x.send();
// 当创建了一个新的xhr对象并发出请求后,应该立即修改标识变量标致当前为正在发送的状态
isSending = true;
x.onreadystatechange = function(){
if(x.readyState === 4){
// 当当前请求返回了所有结果时,应当修改标识变量
isSending = false;
}
}
}
参考:$.get(url,[data],[fn],[type]) | jQuery API 3.2 中文文档 | jQuery API 在线手册
url:待载入页面的URL地址
data:待发送 Key/value 参数。
callback:载入成功时回调函数。
type:返回内容格式,xml, html, script, json, text, _default。
返回值:XMLHttpRequest对象
参考:$.post(url,[data],[fn],[type]) | jQuery API 3.2 中文文档 | jQuery API 在线手册
url:发送请求地址。
data:待发送 Key/value 参数。
callback:发送成功时回调函数。
type:返回内容格式,xml, html, script, json, text, _default。
返回值:XMLHttpRequest对象
// jQuery发送GET请求
$.get('http://127.0.0.1:8000/jquery-server', {a:100, b:200}, function(data){
console.log(data);
},'json');
// jQuery发送POST请求
$.post('http://127.0.0.1:8000/jquery-server', {a:100, b:200}, function(data){
console.log(data);
});
$.ajax()
方法发送请求(通用方法)$.ajax(url,[settings])
url:一个用来包含发送请求的URL字符串。
settings:配置对象,AJAX 请求设置。所有选项都是可选的。
返回值:XMLHttpRequest对象
参考:$.ajax(url,[settings]) | jQuery API 3.2 中文文档 | jQuery API 在线手册
$.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('出错啦!!');
},
//头信息
headers: {
c:300,
d:400
}
});
axios的GitHub官网:
参考:axios/axios: Promise based HTTP client for the browser and node.js
以下概念解释来自百度百科:
Axios是一个基于promise 的 HTTP 库,可以用在浏览器和 node.js中。axios本质上也是对原生XHR的封装,只不过它是Promise的实现版本,符合最新的ES规范。大致有以下特点:
- 从浏览器创建 XMLHttpRequests
- 从 node.js 创建 http 请求
- 支持 Promise API
- 拦截请求和响应
- 转换请求和响应数据
- 取消请求
- 自动转换JSON数据
- 客户端支持防御XSRF
简单理解的话就是:axios是一个前后端进行数据交互的模块(可以用于发送AJAX的工具包),具体就是在前端调用其封装的get或post等接口向后端发送GET或POST等请求并处理后端返回的数据。它是基于Promise的(所以最好先对Promise有一个前置了解再来学习axios)。
Vue官方推荐在使用其框架时使用axios代替原来的vue-resource来完成请求的发送。
安装或引入axios:
在Node.js环境下:
npm install axios
或使用script标签引入:
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.js"></script>
// axios中用于发送各种请求的API
axios.request(config)
axios.get(url[, config])
axios.delete(url[, config])
axios.head(url[, config])
axios.options(url[, config])
axios.post(url[, data[, config]])
axios.put(url[, data[, config]])
axios.patch(url[, data[, config]])
上述的 config
中具体有哪些配置项可以参看其GitHub官网的request-config
描述:
参考:axios/axios: Promise based HTTP client for the browser and node.js#request-config
baseURL
配置baseURL
之后就不用在后续发送请求时填写完整的URL路径了。
axios.defaults.baseURL = 'http://127.0.0.1:8000';
// GET 请求
// 第一个参数是资源路径;第二个参数是相关的配置(是一个对象类型)
// 因为上面配置了baseURL,所以下面的URL参数不需要写完整的资源路径
axios.get('/axios-server', {
// 跟在URL后的参数(相当于是设置请求行中的URL携带的参数)
params: {
id: 100,
vip: 7
},
// 设置请求头信息,支持自定义头信息字段
headers: {
name: 'atguigu',
age: 20
}
}).then(value => {
// 因为axios发送的请求返回的是一个Promise对象,所以可以直接跟上.then()函数处理返回结果
console.log(value);
});
上面的value是请求成功时所返回的一个对象,其中包含服务端返回的响应体data
(会帮我们自动把JSON格式的字符串转换为对象),以及响应头信息headers
,还有响应状态码status
和响应状态字符串statusText
等信息。
具体有哪些信息可以参看其GitHub官网的 #response-schema
描述:
参考:axios/axios: Promise based HTTP client for the browser and node.js#response-schema
注意POST
请求API里面的参数与GET
请求不大一样。
// POST 请求
// 第一个参数是资源路径;第二个参数是请求体的数据(对象类型);第三个参数是相关的配置(对象类型)
axios.post('/axios-server', {
username: 'admin',
password: 'admin'
}, {
// 跟在URL后的参数(相当于是设置请求行中的URL携带的参数)
params: {
id: 200,
vip: 9
},
// 设置请求头信息,支持自定义头信息字段
headers: {
height: 180,
weight: 180,
}
});
axios()
函数发送请求(通用方法)// url是资源路径,config是一个对象类型的配置项
axios(url[, config])
可以类比jQuery中的 $.ajax()
方法发送请求(通用方法)(按住Ctrl键单击即可跳转)。
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.status);
//响应状态字符串
console.log(response.statusText);
//响应头信息
console.log(response.headers);
//响应体
console.log(response.data);
})
可以发现,上面的通用方法就像是按照报文的格式在填写:行、头、体这三部分信息,比较直观。通用方法同样也是返回一个Promise对象,所以可以调用 .then()
对结果进行处理。
有关axios的内容可以到下方的视频中系统学习:
参考:尚硅谷2021最新版axios入门与源码解析_哔哩哔哩_bilibili
fetch
是在ES6中增加的一个JavaScript底层API,其作用与 XMLHttpRequest
对象差不多,官方的MDN文档中也将 fetch
描述为 XMLHttpRequest
的更理想的替代方案,其在功能上可以与AJAX相互替代。但要注意应该把 fetch
和 原生的 AJAX 看做是同一等级的,而不是像axios或jQuery中的相关API那样是对原生AJAX的封装。
而fetch
是基于ES6中的Promise对象的,所以支持链式调用。相较于原生的 XMLHttpRequest
对象,fetch
的代码结构更加的简洁直观,其参数与jQuery中的 $.ajax()
有相似之处。在下方的MDN文档和相关博客中也有这方面的比较。
参考:使用 Fetch - Web API 接口参考 | MDN
参考:ajax和axios、fetch的区别 - 简书
注意:fetch
的兼容性不是那么好,在IE中不兼容,在某些版本的FireFox和移动端浏览器中的支持度也不好。
这里也可以类比jQuery中的 $.ajax()
方法发送请求(通用方法)(按住Ctrl键单击即可跳转)。
fetch(url [,init])
函数可以接受两个参数,第一个url参数指明目标资源路径,然后返回一个包含响应结果的Promise对象;第二个可选参数用于配置一些请求行、头、体之类的信息。
fetch('http://127.0.0.1:8000/fetch-server?vip=10', {
// 设置请求方法
method: 'POST',
// 设置请求头信息,支持自定义头信息字段
headers: {
name:'atguigu'
},
// 设置请求体
body: 'username=admin&password=admin'
}).then(response => {
// 响应对象中的text()方法用于将响应体转化为字符串
// return response.text();
// 响应对象中的json()方法用于将JSON格式的字符串的响应体转化为对象
return response.json();
}).then(response=>{
// 这个链式调用是Promise中的语法,这里不再展开
console.log(response);
});
有关于 fetch
的详细配置描述以及相关的响应对象中的属性描述可以参看MDN官方文档。
参考:使用 Fetch - Web API 接口参考 | MDN
由于兼容性方面的原因,虽然 fetch
也提供了较为直观配置方式和基于Promise的处理方式,但是我们用得更多的还是axios。
同源策略(Same-Origin Policy)最早由 Netscape 公司提出,是浏览器的一种安全策略。
如果两个 URL (格式为:protocol://host:port)的 protocol(协议)、port(端口) (如果有指定的话)和 host(主机IP地址) 都相同的话,则这两个 URL 是同源。相关详细描述可参看官方MDN文档。
参考:浏览器的同源策略 - Web 安全 | MDN
违背上述的同源策略就是跨域。
JSONP(JSON with Padding),是一个非官方的跨域解决方案,纯粹凭借程序员的聪明才智开发出来,只支持 get 请求。
在网页有一些标签天生具有跨域能力,比如:img、link、iframe、script。JSONP 就是利用 script 标签的跨域能力来发送请求的。
当我们在网页中插入 script 标签时,即使 src
属性中所只指向的资源地址与当前网页是跨域的,也能正确执行 src
中的代码。
而 JSONP 正是利用了这一点,在后端代码中返回一个函数的调用(准确的说,返回的应该是一段浏览器能够解析的JS代码),当然,这个函数必须事先已经在前端代码中声明过了,同时传递给该函数的参数就是我们想要返回的数据。
// 后端代码 server.js
// jsonp服务
app.all('/jsonp-server', (request, response) => {
// response.send('console.log("hello jsonp")');
// data是我们想要给前端返回的数据
const data = {
name: '尚硅谷atguigu'
};
// 将数据转化为字符串
let str = JSON.stringify(data);
// 返回结果,注意这里返回的是一个函数调用,其中的str参数才是我们想要返回的数据
response.end(`handle(${str})`);
});
<script>
// 处理数据的函数,注意前端一定要实现声明这个函数,否则下方的script标签返回的代码将没法执行
function handle(data) {
//获取 result 元素
const result = document.getElementById('result');
result.innerHTML = data.name;
}
script>
<script src="http://127.0.0.1:8000/jsonp-server">script>
在一个输入框中输入用户名,当输入框失去焦点时向服务端发送AJAX请求,让服务端校验该用户名是否已经存在,并返回校验结果。前端声明一个 handle()
函数来对返回的结果进行处理。
// 后端代码 server.js
// 用户名检测是否存在
app.all('/check-username', (request, response) => {
// response.send('console.log("hello jsonp")');
const data = {
exist: 1,
msg: '用户名已经存在'
};
// 将数据转化为字符串
let str = JSON.stringify(data);
// 返回结果,注意这里返回的是一个函数调用,其中的str参数才是我们想要返回的数据
response.end(`handle(${str})`);
});
<script>
// 获取 input 元素
const input = document.querySelector('input');
const p = document.querySelector('p');
// 声明 handle 函数用于处理服务端的响应结果
function handle(data){
input.style.border = "solid 1px #f00";
// 修改 p 标签的提示文本
p.innerHTML = data.msg;
}
// 给输入框绑定失去焦点事件
input.onblur = function(){
// 获取用户的输入值
let username = this.value;
// 向服务器端发送请求 检测用户名是否存在
// 1. 创建 script 标签
const script = document.createElement('script');
// 2. 设置标签的 src 属性
script.src = 'http://127.0.0.1:8000/check-username';
// 3. 将 script 插入到文档中
document.body.appendChild(script);
}
script>
注意上面前端代码中插入 script 标签的步骤是我们运用JSONP的一般思路。
使用jQuery发送JSONP请求只需要在前端引入jQuery的情况下调用 $.getJSON(url, callback)
函数即可。
注意下面的前端代码中 $.getJSON(url, callback)
函数中的 url 参数后跟了一个 ?callback=?
参数,这是固定写法。
<script>
$('button').eq(0).click(function(){
// 第一个参数是请求的资源地址;第二个参数是收到响应后的回调函数
$.getJSON('http://127.0.0.1:8000/jquery-jsonp-server?callback=?', function(data){
$('#result').html(`
名称: ${data.name}
校区: ${data.city}
`)
});
});
script>
虽然我们给 callback
参数的初始值为 ?
,但是当jQuery发送请求后,这个参数会被赋予一串以jQuery开头的随机值:
也就是相当于jQuery在前端代码中声明了一个名为 jQueryxxxxxxxxxxxxxx_xxxxxxxx
的函数,而这个函数就是我们上面所写的那个回调函数。而根据JSONP的原理,我们应该在后端代码中返回一个该函数的调用。
我们可以通过 request.query.callback
这个属性接收这个callback参数,然后在响应中返回一个对该函数的调用。
app.all('/jquery-jsonp-server', (request, response) => {
const data = {
name: '尚硅谷',
city: ['北京', '上海', '深圳']
};
// 将数据转化为字符串
let str = JSON.stringify(data);
// 接收 callback 参数
let cb = request.query.callback;
// 返回对callback函数的调用,注意这里用的是end方法,而不是之前的send方法
response.end(`${cb}(${str})`);
});
CORS(Cross-Origin Resource Sharing),跨域资源共享。CORS 是官方的跨域解决方案,它的特点是不需要在客户端做任何特殊的操作,完全在服务器中进行处理,支持 get 和 post 请求。跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。
官方MDN文档是这样描述的:
CORS (Cross-Origin Resource Sharing,跨域资源共享)是一个系统,它由一系列传输的HTTP头组成,这些HTTP头决定浏览器是否阻止前端 JavaScript 代码获取跨域请求的响应。
同源安全策略 默认阻止“跨域”获取资源。但是 CORS 给了web服务器这样的权限,即服务器可以选择,允许跨域请求访问到它们的资源。
其他详细说明请参考:CORS - 术语表 | MDN
简单来说,CORS 是通过设置一个响应头 Access-Control-Allow-Origin
来告诉浏览器,该请求允许跨域,浏览器收到该响应以后就会对响应放行。
用 CORS 解决跨域问题只需要在服务端代码中通过 response.setHeader()
方法进行相应的响应头设置。客户端正常发送请求即可,不需要做相应的设置。
// 后端代码 server.js
app.all('/cors-server', (request, response) => {
//设置响应头
// response.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:5500");
response.setHeader("Access-Control-Allow-Origin", "*");
response.send('hello CORS');
});
如果服务端不设置 Access-Control-Allow-Origin
的话,一旦发生跨域问题就会报以下的错误:
Access to XMLHttpRequest at 'http://127.0.0.1:8000/cors-server' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
响应头 | 作用 |
---|---|
Access-Control-Allow-Origin | 指示请求的资源能共享给哪些域。 “*” 表示允许与来自所有域的请求共享指定的资源 |
Access-Control-Allow-Headers | 用在对预请求的响应中,指示实际的请求中可以使用哪些 HTTP 头。 “*” 表示允许所有类型的请求头(包括自定义头字段) |
Access-Control-Allow-Method | 指定对预请求的响应中,哪些 HTTP 方法允许访问请求的资源。 “*” 表示允许所有类型的请求方法 |
…… | 其他响应头可参看上面提到的CORS官方MDN文档 |
更新:2021年8月19日22:12:59
【参考课程】
参考:【尚硅谷】3小时Ajax入门到精通_哔哩哔哩_bilibili
参考:尚硅谷2021最新版axios入门与源码解析_哔哩哔哩_bilibili
参考:Ajax - Web 开发者指南 | MDN
参考:AJAX - 维基百科,自由的百科全书
参考:理解AJAX - SegmentFault 思否
参考:AJAX - 简书
【什么是异步】
参考:怎样理解阻塞非阻塞与同步异步的区别? - 知乎
参考:同步(Synchronous)和异步(Asynchronous) - myCpC - 博客园
参考:真的理解同步和异步了吗?_Small_White-CSDN博客
更新:2021年8月20日16:51:53
【什么是HTTP】
参考:超文本传输协议 - 维基百科,自由的百科全书
参考:HTTP_百度百科
更新:2021年8月20日20:53:56
参考:HTTP 响应的格式及状态码_简言-CSDN博客_响应状态码
更新:2021年8月21日24:32:39
参考:$.ajax(url,[settings]) | jQuery API 3.2 中文文档 | jQuery API 在线手册
参考:ajax和axios、fetch的区别 - 简书
更新:2021年8月21日09:50:29
【什么是axios】
参考:axios/axios: Promise based HTTP client for the browser and node.js
参考:axios_百度百科
更新:2021年8月21日11:29:18
参考:使用 Fetch - Web API 接口参考 | MDN
更新:2021年8月21日12:43:46
【同源策略和跨域】
参考:浏览器的同源策略 - Web 安全 | MDN
参考:CORS - 术语表 | MDN