跳到主要内容

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 的第一步是创建一个实例:

javascript
const xhr = new XMLHttpRequest();

XMLHttpRequest 的生命周期

XHR 请求有以下几个关键步骤:

配置和发送请求

在发送请求之前,我们需要使用 open() 方法配置请求,然后使用 send() 方法发送请求:

javascript
// 配置 GET 请求
xhr.open('GET', 'https://api.example.com/data', true);

// 发送请求
xhr.send();

open() 方法的参数:

  • 第一个参数:HTTP 请求方法(GET、POST、PUT 等)
  • 第二个参数:URL
  • 第三个参数:是否异步(通常设为 true)

处理响应

要处理服务器响应,我们需要监听 XMLHttpRequest 对象的事件,最常用的是 onreadystatechange 事件:

javascript
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 客户端的状态:

状态描述
0UNSENT已创建 XHR 对象,但未调用 open() 方法
1OPENED已调用 open() 方法
2HEADERS_RECEIVED已调用 send() 方法,收到响应头
3LOADING正在接收响应体
4DONE请求完成

发送不同类型的请求

GET 请求

GET 请求通常用于从服务器获取数据:

javascript
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 请求通常用于向服务器发送数据:

javascript
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 响应

javascript
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 响应

javascript
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
const xmlData = xhr.responseXML;
const items = xmlData.getElementsByTagName('item');
console.log('XML 数据中的项目数量:', items.length);
}
};

处理二进制数据

javascript
xhr.responseType = 'arraybuffer';  // 设置响应类型

xhr.onload = function() {
if (xhr.status === 200) {
const arrayBuffer = xhr.response;
// 处理二进制数据
const byteArray = new Uint8Array(arrayBuffer);
console.log('接收到的字节:', byteArray.length);
}
};

错误处理与超时

处理错误

javascript
xhr.onerror = function() {
console.error('请求发生错误');
};

设置超时

javascript
// 设置 5 秒超时
xhr.timeout = 5000;

xhr.ontimeout = function() {
console.error('请求超时');
};

进度监控

XMLHttpRequest 允许我们监控上传和下载的进度:

javascript
// 监控下载进度
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 从服务器加载用户数据并在页面上显示:

javascript
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 代码:

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 时,应遵循以下最佳实践:

  1. 始终使用异步请求:将 open() 方法的第三个参数设为 true,避免阻塞用户界面。

  2. 妥善处理错误:设置 onerrorontimeout 和状态码检查,确保用户获得良好的错误反馈。

  3. 避免跨域问题:了解同源政策,使用 CORS 或其他技术解决跨域请求问题。

  4. 设置适当的超时:防止请求无限期挂起。

  5. 显示加载状态:使用进度事件提供良好的用户体验。

跨域资源共享 (CORS)

当使用 XMLHttpRequest 发送跨域请求时,可能会遇到同源策略的限制。CORS(Cross-Origin Resource Sharing,跨域资源共享)是一种机制,它使用额外的 HTTP 头来告诉浏览器允许一个域上的 Web 应用访问另一个域上的资源。

跨域请求

跨域请求需要服务器的配合,通过设置 Access-Control-Allow-Origin 等响应头来允许跨域访问。客户端无法单方面解决跨域问题。

XMLHttpRequest vs. Fetch API

虽然 XMLHttpRequest 已经存在很长时间,但现代 JavaScript 应用程序通常使用更新的 Fetch API:

特性XMLHttpRequestFetch API
语法较繁琐,基于事件更简洁,基于 Promise
错误处理通过事件回调使用 Promise 链式调用
取消请求使用 abort() 方法使用 AbortController
进度事件原生支持需要使用 Response.body 和 ReadableStream
浏览器兼容性更好(包括旧浏览器)较新,可能需要 polyfill

总结

XMLHttpRequest 是一个功能强大的 API,使 JavaScript 能够执行异步网络请求。虽然较新的 Fetch API 提供了更现代的接口,但 XMLHttpRequest 在许多项目中仍然有其用武之地,特别是在处理上传进度、支持老旧浏览器或需要中止请求等场景中。

通过学习 XMLHttpRequest,你不仅可以理解 AJAX 的基础工作原理,还能够在需要时灵活运用这一 API 来构建交互性强的 Web 应用程序。

练习

  1. 创建一个简单的表单,使用 XMLHttpRequest 将表单数据以 POST 请求发送到服务器。

  2. 构建一个小型图片库应用,使用 XMLHttpRequest 从服务器加载图片信息,并显示加载进度。

  3. 实现一个具有自动完成功能的搜索框,当用户输入时,使用 XMLHttpRequest 向服务器请求匹配的建议。

  4. 创建一个文件上传器,使用 XMLHttpRequest 的 progress 事件显示上传进度条。

附加资源