## 序幕:两个API的"身世之谜"
在Web开发的江湖里,XMLHttpRequest(简称XHR)就像一位身经百战的老将,而Fetch API则是手持光剑的绝地武士。让我们先来段"DNA检测":
- **XHR(2006出道)**
"AJAX的初恋对象"
特点:事件驱动、回调地狱种子选手、自带进度监控
- **Fetch(2015登场)**
"Promise的模范生"
特点:基于Promise、流式处理专家、CORS配置达人
举个栗子🌰:当你想从服务器获取猫猫图片时
```javascript
// XHR写法(古典派)
const xhr = new XMLHttpRequest();
xhr.open('GET', '/cats');
xhr.onload = () => {
if (xhr.status === 200) {
console.log(JSON.parse(xhr.responseText));
}
};
xhr.send();
// Fetch写法(现代派)
fetch('/cats')
.then(response => response.json())
.then(cats => console.log(cats));
```
## 第一幕:核心机制大揭秘
### 1.1 底层传输的"内功心法"
XHR的"经脉运行图":
```
[初始化] → [配置请求] → [发送] → [onreadystatechange]
→ [状态检测] → [数据处理]
```
Fetch的"真气循环":
```
[Request对象] → [fetch发起] → [Promise链]
→ [Stream处理] → [数据转换]
```
### 1.2 错误处理的"求生指南"
XHR的错误处理像扫雷游戏:
```javascript
xhr.onerror = () => {
console.log('你的网络比蜗牛还慢!');
};
xhr.ontimeout = () => {
console.log('服务器去火星旅游了?');
};
```
Fetch的错误处理像闯关游戏:
```javascript
fetch('/api')
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.catch(error => {
console.log(`捕获到野生错误: ${error}`);
});
```
## 第二幕:六大实战场景终极PK
### 2.1 大文件上传进度条
XHR的"专属技能":
```javascript
xhr.upload.onprogress = (e) => {
const percent = (e.loaded / e.total) * 100;
console.log(`上传进度:${percent}%`);
};
```
Fetch的"黑科技"(通过ReadableStream):
```javascript
const controller = new AbortController();
fetch('/upload', {
method: 'POST',
body: new ReadableStream({
start(controller) {
// 流式写入数据
}
}),
signal: controller.signal
});
```
### 2.2 请求超时控制
XHR的"定时炸弹":
```javascript
xhr.timeout = 5000; // 5秒定时器
xhr.ontimeout = () => {
console.log('请求超时,请检查你的网络!');
};
```
Fetch的"组合技":
```javascript
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('请求超时')), 5000);
});
Promise.race([
fetch('/api'),
timeoutPromise
]).then(/*...*/);
```
## 第三幕:你不知道的高级玩法
### 3.1 请求缓存策略
XHR的"手动挡模式":
```javascript
xhr.open('GET', '/data', true);
xhr.setRequestHeader('Cache-Control', 'max-age=3600');
```
Fetch的"自动驾驶模式":
```javascript
fetch('/data', {
cache: 'force-cache' // 可选值:default/no-store/reload/force-cache
});
```
### 3.2 跨域请求的"安全卫士"
XHR的复杂配置:
```javascript
xhr.withCredentials = true; // 携带cookie
```
Fetch的优雅处理:
```javascript
fetch('/api', {
credentials: 'include', // 可选值: omit/same-origin/include
mode: 'cors'
});
```
## 第四幕:性能优化秘籍
### 4.1 内存管理对比
| 操作 | XHR内存占用 | Fetch内存占用 |
|---------------|-------------|---------------|
| 10MB文件下载 | 10MB | 流式处理≈2MB |
| 1000并发请求 | 高 | 低 |
| 长轮询 | 一般 | 优秀 |
### 4.2 调试技巧宝典
**XHR调试三连:**
1. 在Chrome开发者工具勾选"Log XMLHttpRequests"
2. 使用xhr.addEventListener('readystatechange', debug)
3. 检查readyState的4个阶段
**Fetch调试三式:**
1. 在控制台使用`await fetch(...)`直接查看响应
2. 使用`response.clone()`防止流被消费
3. 配合`navigator.onLine`检测网络状态
## 第五幕:未来战场预测
### 5.1 即将到来的新特性
- **Fetch的"超级进化"**
```javascript
// 实验性功能:优先级设置
fetch('/critical-data', {
priority: 'high' // low/auto/high
});
// 请求保持活跃
fetch('/keepalive', {
keepalive: true
});
```
### 5.2 终极选择指南
| 场景 | 推荐API | 理由 |
|----------------------|---------------|-------------------------------|
| 文件上传进度显示 | XMLHttpRequest | 原生支持上传进度事件 |
| REST API调用 | Fetch | 简洁的Promise链式调用 |
| 需要取消的请求 | Fetch | 配合AbortController更优雅 |
| 兼容IE11 | XMLHttpRequest | Fetch需要polyfill |
## 终章:开发者生存指南
当你在深夜加班时:
- 遇到老项目维护 → 拥抱XHR的兼容性
- 开发新功能 → 投入Fetch的怀抱
- 需要流式处理 → 选择Fetch的ReadableStream
- 要取消请求 → 使用AbortController
最后记住:没有最好的API,只有最合适的场景。就像你不能用瑞士军刀砍树,也不能用电锯开红酒瓶——选对工具才是王道!
**彩蛋:** 试试在控制台输入这个魔法代码,查看浏览器对两种API的支持情况:
```javascript
console.log('XHR支持度:', !!window.XMLHttpRequest);
console.log('Fetch支持度:', !!window.fetch);
```
**如果这篇指南让你少加了一天班,记得点赞关注!下期预告:《WebSocket与SSE:实时通信的冰与火之歌》**