AJAX
1. Ajax 概述
基本概念
AJAX(Asynchronous JavaScript and XML)是一种用于在客户端(通常是浏览器)与服务器之间进行异步通信的技术。
工作原理
Ajax通过XmlHttpRequest对象(XHR)来向服务器发送异步请求,从服务器获得数据后,再用JavaScript来操作DOM(文档对象模型)从而更新页面内容。这种方式使得网页可以在不重新加载整个页面的情况下,实现部分内容的更新,提高了用户体验和网页的交互性。
2. Ajax的基本使用
1. 创建XMLHttpRequest()对象
XMLHttpRequest是Ajax的核心,不同浏览器的创建方式可能不同,现代浏览器通常支持直接使用let xhr = new XMLHttpRequest() 来创建对象,在一些旧版浏览器如IE6可能需要使用 new ActiveObject(“Microsoft.XMLHTTP”)
//现代浏览器可用
let xhr = new XMLHttpRequest();
//旧版浏览器如IE6
let xhr = new ActiveObject("Microsoft.XMLHTTP");
2. 使用xhr对象
1. open()方法
使用open()方法来创建请求,这个方法接收三个参数:指定请求的类型(如GET或POST)、请求的URL以及是否异步处理(布尔值)。下面是一个例子:
//向test.txt发送一个异步的GET请求
xhr.open('get', 'test.txt', true);
关于这行代码,首先,这里的URL是相对于代码所在页面的(即在同一目录中),当然也可以使用绝对的URL。其次,调用open()方法不会实际发送请求,只是为发送请求做好准备。
注意:只能访问同源URL,也就是域名相同、端口相同、协议相同。如果请求的URL域发送请求的页面在任何方面有所不同,则会抛出安全错误。
2. send()方法
做好准备后,调用send()方法发送请求。
//发送请求
xhr.send(null);
send()方法接受一个参数,是作为请求体发送的数据,如果不需要发送请求体,则必须传null,因为这个参数在某些浏览器中是必需的。调用send()之后,请求就会发送到服务器。
3. 处理服务器响应
-
通过监听onreadystatechange事件来处理服务器的响应。当服务器的响应状态发生变化时,会触发该事件。
-
事件处理函数中,可以检查XML对象的readyState属性来判断请求是否完成,以及status属性来判断响应的状态码(如200表示成功)。
-
如果请求成功,可以使用
responseText
或responseXML属性来获取服务器的响应数据。
xhr.onreadystatechagne = function() {
if(xhr.readyState == 4) {
if(xhr.status == 200) {
//xhr.responseText中存储服务器响应后返回的数据
alert(xhr.responseText);
}else {
alert("Response was unsuccessful:" + xhr.status);
}
}
}
3. 第一个ajax案例
到这里,我们已经可以尝试写一个简单的从同目录下的文件中发送请求,下面是一个例子:
- 首先,创建两个文件,第一个是1.txt,第二个是index.html。
- 为1.txt文本文件中添加内容。
- 打开index.html,开始写代码。代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<!-- 用来存放返回的内容 -->
<p id="p1"></p>
<script>
const p1 = document.getElementById("p1");
//一、 创建XMLHttpRequest对象
const xhr = new XMLHttpRequest();
//二、 使用xhr对象
//1. open()创建请求
//第一个参数表示使用GET请求,在同目录下所以写1.txt即可,true表示异步处理
xhr.open("get", "1.txt", true);
//2. send()发送请求
//null这个参数是某些浏览器必需的
xhr.send(null);
//3. 处理服务器响应
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
p1.innerHTML = xhr.responseText;
} else {
console.log("Request was unsuccessful" + xhr.status);
}
}
};
</script>
</body>
</html>
- 使用VS code上的live Sever插件运行html文件。
插件为:
运行效果如下:
页面当中显示了从1.txt文件中响应后返回的数据。
Live Server是一种轻量级的本地开发环境工具,它相当于在您的计算机上启动了一个小型的HTTP服务器,用于实时预览网页内容,尤其是在前端开发中非常实用。
如果你也做到了这一步,那么说明已经实现了第一个简单的ajax技术。后面我们会一起探讨更加丰富的ajax内容。
4. 同步和异步
上面的阐述中,提到了很多次的异步,那么我们就来了解了解什么是异步,同时也了解一下同步的概念。
异步和同步是两种不同的执行方式,它们在多个领域,特别是在计算机科学和软件开发中,有着广泛的应用。
1. 定义
- 同步
在操作系统和应用程序的通信中,同步指同步指的是在通讯的两端所进行的事件必须在相同的时间发生。也就是说,在一个事件进行完之前,不会执行下一个事件。在程序执行中,同步操作意味着程序必须等待当前操作完成并返回结果后才能执行下一步。
- 异步
与同步相反,异步事件的时序不需要互相影响。在程序执行中,异步操作允许程序在发起请求后立即继续执行其他代码,而不必等待响应完成。
2. 特点
- 同步
- **顺序执行:**程序按照代码顺序一步一步执行
- **阻塞:**在执行某个操作时,程序会被阻塞,无法执行其他任务,直到该操作完成。
- **数据一致性:**由于某个操作时,程序会被阻塞,无法执行其他任务,知道该操作完成。
- **适用场景:**适用于需要精确控制程序执行顺序或保证数据一致性的情况,如简单任务、实时性要求高的应用(如用户界面的事件处理)、资源有限的场景。
- 异步
- **并发执行:**程序可以同时处理多个任务。
- **非阻塞:**在执行某个操作时,程序不会被阻塞,可以继续执行其它代码。
- **提高效率和响应性:**由于可以并发执行多个任务,因此提高了程序的效率和响应性。
- **使用场景:**适用于需要处理大量或长时间运行的任务的情况,如网络请求(API调用、数据库查询等)、I/O密集型操作(文件读写、大数据处理等)。
3. 优缺点
-
同步
- **优点:**逻辑清晰,代码结构简单,便于调试和维护;保证了执行的顺序,易于预测程序行为。
- **缺点:**在高延迟操作中,可能导致性能瓶颈,影像系统整体响应速度;在并发处理大量请求时,容易造成资源浪费。
-
异步
- **优点:**提高系统并发性和响应性;优化资源使用,系统能够同时处理多个请求,充分利用CPU和 I/O 资源。
- **缺点:**逻辑复杂,可能导致回调地狱(callback hell),增加代码的复杂性,降低可读性;调试困难,一异步操作的执行顺序不确定,调试和错误处理相对复杂。
4. 实际应用
- 同步
- 在需要精确控制程序执行顺序或保证数据一致性的情况下,使用同步操作更加合适。例如,在数据库事务处理中,通常需要保证多个操作按顺序执行并成功完成,以确保数据的一致性。
- 异步
- 在需要处理大量数据或长时间运行的任务时,使用异步操作可以避免程序的卡顿和崩溃。例如,在Web开发中,处理网络请求时通常会使用异步操作,以避免因等待响应而造成的阻塞,提高页面的响应速度和用户体验。
综上所述,同步和异步各有其特点和适用场景。在实际开发中,需要根据具体情况选择同步或异步操作,以优化程序的性能和用户体验。
5. GET请求
接下来我们来讨论一下GET请求方式。
1. 用途
GET请求主要用于从服务器获取数据。它是HTTP协议中最常见的请求方法之一,用于对服务器资源的请求。此外,它是幂等的,即多次请求相同的资源不会改变服务器的状态。因此,GET请求通常用于查询操作、搜索操作以及读操作等场景。
2. 传参方式与位置
传参时,参数被附加在URL后面,形成一个查询字符串。对 xhr 而言,查询字符串必须正确编码后添加到 URL 后面,然后再传给open()方法。发送 GET 请求最常见的一个错误是查询字符串格式不对。查询字符串中的每个名和值都必须使用encodeURIComponent()编码,所有名/值对必须以和号(&)分隔,如下面的例子所示:
encodeURIComponent()是JavaScript中的一个内置函数,用于对URI(Uniform Resource Identifier,统一资源标识符)组件中的特殊字符进行编码,使其能够在URL中安全地传输。
xhr.open("get", "1.txt?username=zhangsan&password=1111", true);
我们可以封装一个函数将查询字符串参数添加到现有的URL末尾:
/*
三个参数:
url:要添加查询字符串的URL
name:查询参数
value:参数值
*/
function addURLParam(url, name, value) {
//三元运算符,判断url中是否含有“?”,如果没有,就在末尾添加一个;如果有,就在末尾添加一个"&"。
url += (url.indexOf("?") == -1 ? "?" : "&");
//添加完"?"或者"&"后,后面跟上encodeURIComponent()编码格式的键值对
url += encodeURIComponent(name) + "=" + encodeURIComponent(value);
//返回更新后的URL
return url;
}
可以使用这个函数构建请求 URL,如下面的例子所示:
let url = "1.txt";
//添加参数
url = addURLParam(url,"name","zhangsan");
url = addURLParam(url,"age",18);
//初始化请求
xhr.open("get",url,true);
然后我们可以改造一下这个函数,从添加一个参数,改造成添加多个参数,如下:
/*
两个参数
url:要添加查询字符串的URL
params:要添加的所有键值对参数的对象
*/
function addURLParams(url, params) {
//添加完"?"或者"&"后,后面跟上encodeURIComponent()编码格式的键值对
for (let param in params) {
//三元运算符,判断url中是否含有“?”,如果没有,就在末尾添加一个;如果有,就在末尾添加一个"&"。
url += url.indexOf("?") == -1 ? "?" : "&";
url += encodeURIComponent(param) + "=" + encodeURIComponent(params[param]);
}
return url;
}
案例如下:
let url = "1.txt";
let params = {
username: "zhangsan",
age: 18,
passwrod: "zyup1234"
};
//添加参数
url = addURLParams(url,params);
//初始化请求
xhr.open("get",url,true);
3. 安全性
GET请求的参数直接暴露在URL上,因此不能用来传递敏感信息(如密码、密钥等)。如果需要传递敏感信息,应使用POST请求或其他安全的传输方式。
4. 缓存与浏览器行为
由于参数在URL中,因此GET请求可以被缓存、被书签保存,并可被浏览器历史记录保存。这有助于加快页面的加载速度,但也可能导致数据泄露或被恶意利用。此外,因为GET请求的参数被缓存,所以如果更新数据后,再次获取数据,其实在短时间内是从缓存中获取,不会去发送请求再次获取新数据。如果是老的浏览器,那么重新发送请求的时间会变长,各种浏览器变长的时间不一致,案例如下:
在同目录下放1.txt文件和readtxt.html文件。内容分别如下:
1.txt:
readtxt.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<p id="p"></p>
<input type="button" value="读取" />
<script src="ajax.js"></script>
<script>
const p = document.getElementById("p");
const btn = document.querySelector("input");
btn.addEventListener("click", function () {
const xhr = new XMLHttpRequest();
xhr.open("get", "1.txt", true);
xhr.send(null);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
p.innerHTML = xhr.responseText;
}
}
};
});
</script>
</body>
</html>
打开页面后,点击读取:
之后更新1.txt中的数据
再次点击读取,第一次点击没有效果,多点几次才会出现新的内容。
多点几次之后:
解决办法:
在url地址后面更新参数即可。我们通常会用时间戳的方式去更新数据。
// new Date().getDate() 获取当前时间戳
xhr.open("get", "1.txt?t=" + new Date().getDate(), true);
5. 数据类型限制
只能接受ASCII字符作为参数。如果需要传递非ASCII字符(如中文),则需要进行encodeURIComponent编码操作。
6. 数据包与TCP连接
通常只产生一个TCP数据包,因为他讲参数附加在URL后面进行传输。
TCP(Transmission Control Protocol)数据包是TCP通信机制中的一个关键概念,它是在网络通信中传输数据的基本单位。
6. POST请求
1. 用途
POST请求在Web开发中扮演着重要的角色,其用途涵盖了数据提交、文件上传、资源创建、敏感操作执行、大量数据发送以及触发服务器操作等多个方面。
2. 传参方式与位置
传参时,参数被放在HTTP请求体中传输。这种方式不会将参数暴露在URL中,因此相对更安全。此外,POST请求没有数据长度的限制,可以传输大量数据。每个 POST 请求都应该在请求体中携带提交的数据,而 GET 请求则不然。POST 请求的请求体可以包含非常多的数据,而且数据可以是任意格式。要初始化 POST 请求,open()方法的第一个参数要传"post",例如:
xhr.open("post","1.txt",true);
默认情况下,对服务器而言,POST 请求与提交表单是不一样的。服务器逻辑需要读取原始 POST数据才能取得浏览器发送的数据。其中需要将Content-Type头部设置为发送数据的相应格式,并在send方法中输入要发送的数据。
//将发送的数据设置为json格式
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send({"username":"zhangsan123","password":"123456"});
下面是一个注册案例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<form action="javascript:;" id="user-info">
<input type="text" id="username" name="username" placeholder="请输入用户名" /><br />
<input type="password" id="password" name="passwrod" placeholder="请输入密码" /><br />
<input type="button" id="btn" name="register" value="注册" />
</form>
<script>
const btn = document.getElementById("btn");
const username = document.getElementById("username");
const password = document.getElementById("password");
btn.addEventListener("click", function () {
//使用datas对象存放要发送的数据
const datas = {
username: username.value,
password: password.value
};
//创建XMLHttpRequest对象
const xhr = new XMLHttpRequest();
//初始化请求
xhr.open("post", "https://hmajax.itheima.net/api/register");
//设置请求头
xhr.setRequestHeader("Content-Type", "application/json");
//发送请求并携带发送数据,其中JSON.stringify()将对象格式转化为JSON格式
xhr.send(JSON.stringify(datas));
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
console.log(xhr.response);
} else {
console.log(xhr.tatus);
}
}
};
});
</script>
</body>
</html>
3. 安全性
参数不会暴露在URL中,因此相对更安全。它更适合传输敏感数据。然而,从传输角度来说,无论是GET还是POST,如果不使用HTTPS进行加密传输,数据仍然可能被截获和篡改。因此,在实际应用中,应使用HTTPS来确保数据传输的安全性。
4. 缓存与浏览器行为
由于参数在请求体中,因此POST请求不会被缓存、不会被保存在浏览器历史记录或书签中。这有助于保护数据的隐私性,但也可能导致页面加载速度较慢,因为每次都需要向服务器发送请求。
5. 数据类型限制
对参数类型没有限制,可以传输任何类型的数据(如文本、图片、音频等)。这使得POST请求在处理复杂数据类型时更具灵活性。此外,POST请求没有数据长度的限制,可以传输大量数据。
6. 数据包与TCP连接
可能产生两个TCP数据包。第一个数据包用于发送请求头和部分请求体(如果请求体较大,则可能需要多个数据包来传输),第二个数据包用于发送剩余的请求体(如果有的话)。这使得POST请求在处理大量数据时可能需要更多的时间和资源。从性能方面说,发送相同数量的数据,GET 请求比 POST 请求要快两倍。
7. 请求头
请求头(Request Headers)是用来传递客户端信息和配置给服务器的一组键值对。这些请求头可以帮助服务器理解请求的性质、客户端的期望以及如何处理响应。以下是一些常见的AJAX请求头及其用途:
1. 常见的AJAX请求头及其用途
1. Content-Type
-
描述发送的数据类型。例如:
-
application/json
:表示发送的是JSON格式的数据。 -
application/x-www-form-urlencoded
:表示发送的是表单数据,键值对用&
连接,键和值用=
连接。 -
multipart/form-data
:通常用于文件上传。
-
2. Authorization
- 包含身份验证信息,如Bearer Token、Basic Auth等。例如:
Bearer <token>
:用于传递JWT(JSON Web Token)。Basic <encoded-value>
:用于传递Base64编码的用户名和密码。
3. Accept
- 告诉服务器客户端能够处理的内容类型。例如:
application/json
:表示客户端期望接收JSON格式的数据。text/html
:表示客户端期望接收HTML格式的数据。
4. X-Requested-With
- 通常用来标识AJAX请求。例如:
XMLHttpRequest
:传统的AJAX请求会设置这个头。
5. If-Modified-Since
- 用于条件GET请求,如果资源自指定日期以来未被修改,服务器可以返回304状态码而不是返回资源。
6. Referer
- 表示当前请求是从哪个页面链接过来的。可以用于防盗链或分析用户来源。
7. User-Agent
- 包含了发出请求的浏览器或其他客户端的信息,可以用于服务器统计或定制响应。
2. 设置请求头
在JavaScript中,你可以使用XMLHttpRequest
对象或更现代的fetch
API来设置请求头。
Fetch API 是现代 Web 应用程序中用于执行 HTTP 请求的新标准接口。它提供了一个全局
fetch
函数,以及相关的Response
、Request
和Headers
等接口,用于简化异步网络请求并处理响应。
1. 使用XMLHttpRequest
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Authorization', 'Bearer <token>');
2. 使用fetch
javascript
fetch('1.txt', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer <token>'
}
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
3. 注意事项
- CORS(跨域资源共享):当你向不同域的服务器发送请求时,服务器必须配置CORS策略来允许这些请求,否则浏览器会阻止请求。
- 安全性:不要在请求头中发送敏感信息,如密码,除非通过安全的连接(HTTPS)发送。
- 缓存:某些请求头(如
Cache-Control
)可以影响浏览器或服务器对请求的缓存行为。
通过理解和正确设置请求头,你可以更有效地与服务器进行通信,并确保数据以预期的方式传输和处理。