JavaScript XMLHttpRequest
什么是 XMLHttpRequest?
XMLHttpRequest (XHR) 是一种浏览器 API,允许客户端 JavaScript 与服务器进行网络通信,而无需刷新整个页面。它是 AJAX(异步 JavaScript 和 XML)技术的基础,使得网页可以在不刷新整个页面的情况下更新部分内容。
尽管名称中包含"XML",但 XMLHttpRequest 可以用于传输任何类型的数据,不仅仅是 XML,如 JSON、HTML、纯文本等。
XMLHttpRequest 最初由微软在 Internet Explorer 5 中引入,后来被其他浏览器采用并标准化。虽然现在有更现代的 Fetch API,但 XMLHttpRequest 仍然广泛使用,尤其是在需要支持旧浏览器的项目中。
XMLHttpRequest 基础
创建 XMLHttpRequest 对象
使用 XMLHttpRequest 的第一步是创建一个实例:
const xhr = new XMLHttpRequest();
XMLHttpRequest 的生命周期
XHR 请求有以下几个关键步骤:
配置和发送请求
在发送请求之前,我们需要使用 open()
方法配置请求,然后使用 send()
方法发送请求:
// 配置 GET 请求
xhr.open('GET', 'https://api.example.com/data', true);
// 发送请求
xhr.send();
open()
方法的参数:
- 第一个参数:HTTP 请求方法(GET、POST、PUT 等)
- 第二个参数:URL
- 第三个参数:是否异步(通常设为 true)
处理响应
要处理服务器响应,我们需要监听 XMLHttpRequest 对象的事件,最常用的是 onreadystatechange
事件:
xhr.onreadystatechange = function() {
// readyState 为 4 表示请求完成
if (xhr.readyState === 4) {
// HTTP 状态码为 200 表示成功
if (xhr.status === 200) {
console.log('响应成功:', xhr.responseText);
} else {
console.error('请求失败,状态码:', xhr.status);
}
}
};
readyState 的值
readyState
属性表示 XMLHttpRequest 客户端的状态:
值 | 状态 | 描述 |
---|---|---|
0 | UNSENT | 已创建 XHR 对象,但未调用 open() 方法 |
1 | OPENED | 已调用 open() 方法 |
2 | HEADERS_RECEIVED | 已调用 send() 方法,收到响应头 |
3 | LOADING | 正在接收响应体 |
4 | DONE | 请求完成 |
发送不同类型的请求
GET 请求
GET 请求通常用于从服务器获取数据:
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data?id=123', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
const response = JSON.parse(xhr.responseText);
console.log(response);
}
};
xhr.send();
POST 请求
POST 请求通常用于向服务器发送数据:
const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://api.example.com/submit', true);
// 设置请求头
xhr.setRequestHeader('Content-Type', 'application/json');
// 监听状态变化
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log('数据提交成功!');
console.log(xhr.responseText);
}
};
// 准备要发送的数据
const data = {
name: '张三',
email: 'zhangsan@example.com'
};
// 发送数据
xhr.send(JSON.stringify(data));
处理不同类型的响应
XMLHttpRequest 可以处理多种类型的响应:
处理 JSON 响应
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
try {
const jsonData = JSON.parse(xhr.responseText);
console.log('解析的 JSON 数据:', jsonData);
} catch(e) {
console.error('解析 JSON 失败:', e);
}
}
};
处理 XML 响应
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
const xmlData = xhr.responseXML;
const items = xmlData.getElementsByTagName('item');
console.log('XML 数据中的项目数量:', items.length);
}
};
处理二进制数据
xhr.responseType = 'arraybuffer'; // 设置响应类型
xhr.onload = function() {
if (xhr.status === 200) {
const arrayBuffer = xhr.response;
// 处理二进制数据
const byteArray = new Uint8Array(arrayBuffer);
console.log('接收到的字节:', byteArray.length);
}
};
错误处理与超时
处理错误
xhr.onerror = function() {
console.error('请求发生错误');
};
设置超时
// 设置 5 秒超时
xhr.timeout = 5000;
xhr.ontimeout = function() {
console.error('请求超时');
};
进度监控
XMLHttpRequest 允许我们监控上传和下载的进度:
// 监控下载进度
xhr.onprogress = function(event) {
if (event.lengthComputable) {
const percentComplete = (event.loaded / event.total) * 100;
console.log(`已完成: ${percentComplete.toFixed(2)}%`);
}
};
// 监控上传进度
xhr.upload.onprogress = function(event) {
if (event.lengthComputable) {
const percentComplete = (event.loaded / event.total) * 100;
console.log(`上传进度: ${percentComplete.toFixed(2)}%`);
}
};
实际案例:加载用户数据
以下是一个完整的实际案例,展示如何使用 XMLHttpRequest 从服务器加载用户数据并在页面上显示:
function loadUserData() {
const xhr = new XMLHttpRequest();
const url = 'https://jsonplaceholder.typicode.com/users';
// 显示加载消息
document.getElementById('result').innerHTML = '加载中...';
xhr.open('GET', url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
try {
const users = JSON.parse(xhr.responseText);
displayUsers(users);
} catch(e) {
document.getElementById('result').innerHTML = '解析数据出错';
console.error('解析数据出错:', e);
}
} else {
document.getElementById('result').innerHTML = `请求失败,状态码: ${xhr.status}`;
}
}
};
xhr.onerror = function() {
document.getElementById('result').innerHTML = '网络错误,请稍后再试';
};
xhr.timeout = 10000; // 10 秒超时
xhr.ontimeout = function() {
document.getElementById('result').innerHTML = '请求超时,请稍后再试';
};
xhr.send();
}
function displayUsers(users) {
let html = '<h2>用户列表</h2><ul>';
users.forEach(user => {
html += `
<li>
<strong>${user.name}</strong><br />
Email: ${user.email}<br />
Phone: ${user.phone}<br />
Company: ${user.company.name}
</li>
`;
});
html += '</ul>';
document.getElementById('result').innerHTML = html;
}
// 调用函数加载数据
loadUserData();
相应的 HTML 代码:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>XMLHttpRequest 示例</title>
<style>
ul { list-style-type: none; padding: 0; }
li { padding: 10px; margin-bottom: 10px; border: 1px solid #ddd; border-radius: 4px; }
</style>
</head>
<body>
<h1>用户数据</h1>
<div id="result">点击按钮加载数据...</div>
<button onclick="loadUserData()">加载用户数据</button>
<script src="script.js"></script>
</body>
</html>
最佳实践
使用 XMLHttpRequest 时,应遵循以下最佳实践:
-
始终使用异步请求:将
open()
方法的第三个参数设为true
,避免阻塞用户界面。 -
妥善处理错误:设置
onerror
、ontimeout
和状态码检查,确保用户获得良好的错误反馈。 -
避免跨域问题:了解同源政策,使用 CORS 或其他技术解决跨域请求问题。
-
设置适当的超时:防止请求无限期挂起。
-
显示加载状态:使用进度事件提供良好的用户体验。
跨域资源共享 (CORS)
当使用 XMLHttpRequest 发送跨域请求时,可能会遇到同源策略的限制。CORS(Cross-Origin Resource Sharing,跨域资源共享)是一种机制,它使用额外的 HTTP 头来告诉浏览器允许一个域上的 Web 应用访问另一个域上的资源。
跨域请求需要服务器的配合,通过设置 Access-Control-Allow-Origin
等响应头来允许跨域访问。客户端无法单方面解决跨域问题。
XMLHttpRequest vs. Fetch API
虽然 XMLHttpRequest 已经存在很长时间,但现代 JavaScript 应用程序通常使用更新的 Fetch API:
特性 | XMLHttpRequest | Fetch API |
---|---|---|
语法 | 较繁琐,基于事件 | 更简洁,基于 Promise |
错误处理 | 通过事件回调 | 使用 Promise 链式调用 |
取消请求 | 使用 abort() 方法 | 使用 AbortController |
进度事件 | 原生支持 | 需要使用 Response.body 和 ReadableStream |
浏览器兼容性 | 更好(包括旧浏览器) | 较新,可能需要 polyfill |
总结
XMLHttpRequest 是一个功能强大的 API,使 JavaScript 能够执行异步网络请求。虽然较新的 Fetch API 提供了更现代的接口,但 XMLHttpRequest 在许多项目中仍然有其用武之地,特别是在处理上传进度、支持老旧浏览器或需要中止请求等场景中。
通过学习 XMLHttpRequest,你不仅可以理解 AJAX 的基础工作原理,还能够在需要时灵活运用这一 API 来构建交互性强的 Web 应用程序。
练习
-
创建一个简单的表单,使用 XMLHttpRequest 将表单数据以 POST 请求发送到服务器。
-
构建一个小型图片库应用,使用 XMLHttpRequest 从服务器加载图片信息,并显示加载进度。
-
实现一个具有自动完成功能的搜索框,当用户输入时,使用 XMLHttpRequest 向服务器请求匹配的建议。
-
创建一个文件上传器,使用 XMLHttpRequest 的 progress 事件显示上传进度条。