在前端开发中,异步与服务器交互是实现动态页面的核心能力,XMLHttpRequest(XHR) 和 Fetch API 是实现该能力的两大核心工具。本教程将从基础使用、核心差异、实战选择三个维度,带大家掌握这两种工具的用法,助力实际项目开发。
一、XHR:老牌请求工具的实操指南
XHR 是浏览器原生 API,自 1999 年 IE5 引入后,长期支撑老旧项目的异步请求需求。它的优势是能精细控制请求全流程,下面通过完整代码示例,讲解其核心使用步骤。
1.1 XHR 基础使用步骤(以 GET 请求为例)
步骤 1:创建 XHR 实例
通过 new XMLHttpRequest() 创建请求对象,这是发起请求的基础:
// 1. 创建 XHR 实例
const xhr = new XMLHttpRequest();
步骤 2:配置请求参数(open 方法)
使用 open(method, url, async) 定义请求类型、地址和是否异步(默认 true 异步):
// 2. 配置请求:GET 方法,请求地址,异步模式
步骤 3:设置请求头(可选)
若服务器需要特定请求头(如数据格式、身份验证),用 setRequestHeader(key, value) 配置:
步骤 4:监听请求状态变化(onreadystatechange)
通过 onreadystatechange 事件监控请求进度,readyState 表示请求状态,status 表示 HTTP 状态码(200-299 为成功):
// 4. 监听请求状态变化,处理响应
xhr.onreadystatechange = function() {
// readyState === 4 表示请求已完成,status === 200 表示请求成功
if (xhr.readyState === 4 && xhr.status >= 200 && xhr.status ) {
// 解析响应数据(根据服务器返回格式选择 responseText 或 responseXML)
const responseData = JSON.parse(xhr.responseText);
console.log('请求成功,数据:', responseData);
} else if (xhr.readyState === 4) {
// 请求完成但失败(如 404、500)
console.error('请求失败,状态码:', xhr.status);
}
};
步骤 5:发送请求(send 方法)
调用 send(body) 发送请求,GET 请求无需传参(传 null 即可),POST 请求需传入请求体:
// 5. 发送请求(GET 请求传 null)
xhr.send(null);
1.2 XHR 进阶功能(实战常用)
(1)监控上传 / 下载进度(onprogress)
通过 onprogress 事件实现进度条功能,适用于大文件上传 / 下载:
// 下载进度(服务器返回数据时触发)
xhr.onprogress = function(e) {
if (e.lengthComputable) {
// 进度百分比 = 已加载字节 / 总字节 * 100
const progress = (e.loaded / e.total) * 100;
console.log('下载进度:', progress.toFixed(2) + '%');
// 可同步更新页面进度条 DOM
// document.getElementById('progress').style.width = progress + '%';
}
};
// 上传进度(仅 POST/PUT 等带请求体的方法生效)
xhr.upload.onprogress = function(e) {
if (e.lengthComputable) {
const progress = (e.loaded / e.total) * 100;
(2)中断请求(abort 方法)
若需取消正在进行的请求(如用户关闭弹窗),调用 abort() 即可:
// 示例:3 秒后中断请求
setTimeout(() => {
xhr.abort();
console.log('请求已中断');
}, 3000);
1.3 XHR 优缺点总结
- 优点:兼容性好(支持所有老旧浏览器)、支持进度监听和请求中断、可精细控制请求头。
- 缺点:基于事件回调,复杂逻辑易陷入 “回调地狱”(如多个请求依赖时)、代码冗余。
二、Fetch API:现代请求方案的实操指南
Fetch API 是 ES6 新增的现代方案,基于 Promise 实现,代码更简洁,支持流式处理。下面通过实战代码,讲解其基础用法和注意事项。
2.1 Fetch 基础使用步骤(以 GET 请求为例)
步骤 1:调用 fetch 方法发起请求
fetch(url, options) 接收两个参数:请求地址和配置对象(可选),返回 Promise 对象:
.then(response => {
// 步骤 2:判断请求是否成功(response.ok 等价于 status 200-299)
if (!response.ok) {
// 手动抛出错误,进入 catch 环节
throw new Error(`请求失败,状态码:${response.status}`);
}
// 步骤 3:解析响应数据(根据格式选择 json()/text()/blob())
return response.json(); // 解析为 JSON 格式,返回 Promise
})
.then(data => {
// 步骤 4:处理解析后的数据
console.log('请求成功,数据:', data);
})
.catch(error => {
// 步骤 5:捕获错误(仅网络错误或手动抛出的错误)
console.error('请求异常:', error.message);
});
2.2 Fetch 进阶配置(POST 请求 + 自定义参数)
若需发起 POST 请求、携带 Cookie 或自定义请求头,需在 options 中配置:
fetch('https://api.example.com/submit', {
method: 'POST', // 请求方法:GET/POST/PUT/DELETE 等
headers: {
'Content-Type': 'application/json', // 数据格式
'Authorization': 'Bearer your-token' // 身份验证
},
credentials: 'include', // 携带 Cookie(same-origin 仅同域,include 跨域也携带)
body: JSON.stringify({ name: '张三', age: 20 }) // 请求体(需转为 JSON 字符串)
})
.then(response => {
if (!response.ok) throw new Error('提交失败');
return response.text(); // 解析为文本格式
})
.then(result => console.log('提交成功:', result))
.catch(error => console.error('提交异常:', error));
2.3 Fetch 关键注意事项(避坑指南)
- 错误处理逻辑:仅网络错误(如断网、域名无效)会触发 catch,4xx/5xx 状态码需通过 response.ok 手动判断。
- Cookie 携带:默认不携带 Cookie,需手动设置 credentials: 'include' 或 'same-origin'。
- 流式响应处理:支持分段处理大文件,示例如下:
fetch('https://api.example.com/large-file')
.then(response => {
// 获取响应流的读取器
const reader = response.body.getReader();
// 循环读取数据块
const readChunk = async () => {
const { done, value } = await reader.read();
if (done) {
console.log('文件读取完成');
return;
}
// 处理当前数据块(如写入本地缓存)
console.log('读取数据块大小:', value.length);
readChunk(); // 继续读取下一块
};
readChunk();
});
三、XHR 与 Fetch API 核心差异对比(表格版)
四、实战选择指南(按场景匹配)
4.1 优先选 XHR 的场景
- 维护老旧项目:需兼容 IE 浏览器或低版本 Chrome/Firefox。
- 需精细控制请求:如监控上传 / 下载进度、随时中断请求(无需额外代码)。
- 依赖 JSONP 跨域:部分老接口仅支持 JSONP,Fetch 不兼容该方案。
4.2 优先选 Fetch API 的场景
- 开发新项目:目标浏览器为现代浏览器(如 Chrome 60+、Firefox 55+)。
- 处理大文件:需流式读取数据(如视频分段加载、大文件上传)。
- 追求代码简洁:希望减少回调嵌套,用 Promise 简化异步逻辑。
4.3 推荐使用封装库(提升效率)
实际开发中,直接使用原生 API 可能需要处理兼容和异常,推荐使用成熟的封装库:
- 基于 XHR 的库:Axios(支持拦截器、请求取消、自动转换 JSON,兼容性好)。
- 基于 Fetch 的库:node-fetch(浏览器 / Node.js 通用)、whatwg-fetch(补足 Fetch 兼容性)
在前端开发中,异步与服务器交互是实现动态页面的核心能力,XMLHttpRequest(XHR) 和 Fetch API 是实现该能力的两大核心工具。本教程将从基础使用、核心差异、实战选择三个维度,带大家掌握这两种工具的用法,助力实际项目开发。
一、XHR:老牌请求工具的实操指南
XHR 是浏览器原生 API,自 1999 年 IE5 引入后,长期支撑老旧项目的异步请求需求。它的优势是能精细控制请求全流程,下面通过完整代码示例,讲解其核心使用步骤。
1.1 XHR 基础使用步骤(以 GET 请求为例)
步骤 1:创建 XHR 实例
通过 new XMLHttpRequest() 创建请求对象,这是发起请求的基础:
// 1. 创建 XHR 实例
const xhr = new XMLHttpRequest();
步骤 2:配置请求参数(open 方法)
使用 open(method, url, async) 定义请求类型、地址和是否异步(默认 true 异步):
// 2. 配置请求:GET 方法,请求地址,异步模式
步骤 3:设置请求头(可选)
若服务器需要特定请求头(如数据格式、身份验证),用 setRequestHeader(key, value) 配置:
步骤 4:监听请求状态变化(onreadystatechange)
通过 onreadystatechange 事件监控请求进度,readyState 表示请求状态,status 表示 HTTP 状态码(200-299 为成功):
// 4. 监听请求状态变化,处理响应
xhr.onreadystatechange = function() {
// readyState === 4 表示请求已完成,status === 200 表示请求成功
if (xhr.readyState === 4 && xhr.status >= 200 && xhr.status ) {
// 解析响应数据(根据服务器返回格式选择 responseText 或 responseXML)
const responseData = JSON.parse(xhr.responseText);
console.log('请求成功,数据:', responseData);
} else if (xhr.readyState === 4) {
// 请求完成但失败(如 404、500)
console.error('请求失败,状态码:', xhr.status);
}
};
步骤 5:发送请求(send 方法)
调用 send(body) 发送请求,GET 请求无需传参(传 null 即可),POST 请求需传入请求体:
// 5. 发送请求(GET 请求传 null)
xhr.send(null);
1.2 XHR 进阶功能(实战常用)
(1)监控上传 / 下载进度(onprogress)
通过 onprogress 事件实现进度条功能,适用于大文件上传 / 下载:
// 下载进度(服务器返回数据时触发)
xhr.onprogress = function(e) {
if (e.lengthComputable) {
// 进度百分比 = 已加载字节 / 总字节 * 100
const progress = (e.loaded / e.total) * 100;
console.log('下载进度:', progress.toFixed(2) + '%');
// 可同步更新页面进度条 DOM
// document.getElementById('progress').style.width = progress + '%';
}
};
// 上传进度(仅 POST/PUT 等带请求体的方法生效)
xhr.upload.onprogress = function(e) {
if (e.lengthComputable) {
const progress = (e.loaded / e.total) * 100;
(2)中断请求(abort 方法)
若需取消正在进行的请求(如用户关闭弹窗),调用 abort() 即可:
// 示例:3 秒后中断请求
setTimeout(() => {
xhr.abort();
console.log('请求已中断');
}, 3000);
1.3 XHR 优缺点总结
二、Fetch API:现代请求方案的实操指南
Fetch API 是 ES6 新增的现代方案,基于 Promise 实现,代码更简洁,支持流式处理。下面通过实战代码,讲解其基础用法和注意事项。
2.1 Fetch 基础使用步骤(以 GET 请求为例)
步骤 1:调用 fetch 方法发起请求
fetch(url, options) 接收两个参数:请求地址和配置对象(可选),返回 Promise 对象:
.then(response => {
// 步骤 2:判断请求是否成功(response.ok 等价于 status 200-299)
if (!response.ok) {
// 手动抛出错误,进入 catch 环节
throw new Error(`请求失败,状态码:${response.status}`);
}
// 步骤 3:解析响应数据(根据格式选择 json()/text()/blob())
return response.json(); // 解析为 JSON 格式,返回 Promise
})
.then(data => {
// 步骤 4:处理解析后的数据
console.log('请求成功,数据:', data);
})
.catch(error => {
// 步骤 5:捕获错误(仅网络错误或手动抛出的错误)
console.error('请求异常:', error.message);
});
2.2 Fetch 进阶配置(POST 请求 + 自定义参数)
若需发起 POST 请求、携带 Cookie 或自定义请求头,需在 options 中配置:
fetch('https://api.example.com/submit', {
method: 'POST', // 请求方法:GET/POST/PUT/DELETE 等
headers: {
'Content-Type': 'application/json', // 数据格式
'Authorization': 'Bearer your-token' // 身份验证
},
credentials: 'include', // 携带 Cookie(same-origin 仅同域,include 跨域也携带)
body: JSON.stringify({ name: '张三', age: 20 }) // 请求体(需转为 JSON 字符串)
})
.then(response => {
if (!response.ok) throw new Error('提交失败');
return response.text(); // 解析为文本格式
})
.then(result => console.log('提交成功:', result))
.catch(error => console.error('提交异常:', error));
2.3 Fetch 关键注意事项(避坑指南)
fetch('https://api.example.com/large-file')
.then(response => {
// 获取响应流的读取器
const reader = response.body.getReader();
// 循环读取数据块
const readChunk = async () => {
const { done, value } = await reader.read();
if (done) {
console.log('文件读取完成');
return;
}
// 处理当前数据块(如写入本地缓存)
console.log('读取数据块大小:', value.length);
readChunk(); // 继续读取下一块
};
readChunk();
});
三、XHR 与 Fetch API 核心差异对比(表格版)
四、实战选择指南(按场景匹配)
4.1 优先选 XHR 的场景
4.2 优先选 Fetch API 的场景
4.3 推荐使用封装库(提升效率)
实际开发中,直接使用原生 API 可能需要处理兼容和异常,推荐使用成熟的封装库: