在现代 Web 开发中,前端与后端的数据交互是构建动态应用的核心。围绕这一需求,诞生了多个关键技术与工具:XMLHttpRequest(XHR) 、AJAX 、Axios 和 Fetch API。它们之间既有历史演进关系,也有功能重叠与互补。本文将系统梳理四者的关系,深入剖析 XHR 的工作机制与 Fetch 的底层原理,并结合 Vue 3 开发实践,提供一套完整的前端网络通信知识体系。
一、核心概念与层级关系
1.1 AJAX:一种编程范式(不是技术)
- 全称:Asynchronous JavaScript and XML
- 本质 :一种开发模式,指在不刷新页面的情况下,通过 JavaScript 异步与服务器交换数据并更新部分网页内容。
- 核心思想:解耦 UI 更新与数据获取,提升用户体验。
✅ 关键点 :
AJAX 不是某个具体 API,而是一种使用现有技术实现异步通信的策略 。
实现 AJAX 的核心技术就是
XMLHttpRequest。
1.2 XMLHttpRequest(XHR):浏览器原生 API
-
角色 :实现 AJAX 的底层工具
-
功能:提供浏览器与服务器进行 HTTP 通信的能力
-
特点:
- 基于回调(事件驱动)
- 支持进度监控、取消请求、上传/下载
- 兼容性极好(IE7+)
📌 关系 :
XHR 是 AJAX 的"引擎" 。没有 XHR,就没有现代意义上的 AJAX。
1.3 Axios:基于 Promise 的 HTTP 客户端库
-
定位 :对 XHR 的封装与增强
-
核心特性:
- 返回 Promise,支持 async/await
- 自动转换 JSON 数据
- 拦截器(请求/响应)
- 客户端支持 XSRF 防护
- 浏览器 + Node.js 双端支持
-
底层实现 :在浏览器中默认使用 XHR ,在 Node.js 中使用
http模块
scss
// Axios 内部简化逻辑
function axios(config) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(config.method, config.url);
xhr.send(config.data);
xhr.onload = () => resolve(xhr.response);
xhr.onerror = () => reject(xhr.statusText);
});
}
✅ 关系 :
Axios 是 XHR 的现代化封装,让开发者用更简洁的语法享受 XHR 的全部能力。
1.4 Fetch API:浏览器新一代原生 API
-
定位 :XHR 的官方继任者
-
设计目标:
- 基于 Promise,符合现代 JS 编程习惯
- 更简洁的 API 设计
- 更好的流(Stream)支持
- 统一请求/响应模型(Request/Response 对象)
-
底层实现 :并非基于 XHR ,而是直接调用浏览器的网络层(如 Chromium 的
blink::WebURLLoader)
⚠️ 重要区别 :
Fetch 不是 XHR 的封装 ,而是全新的底层实现。
二、四者关系图谱
scss
┌──────────────┐
│ AJAX │ ←── 编程范式(异步通信思想)
└──────┬───────┘
│
┌───────────────────────┼───────────────────────┐
│ │ │
┌────────▼────────┐ ┌──────────▼──────────┐ ┌────────▼────────┐
│ XMLHttpRequest │ │ Fetch API │ │ Axios │
│ (原生, 回调式) │ │ (原生, Promise式) │ │ (第三方库, Promise)│
└────────┬────────┘ └─────────────────────┘ └────────┬────────┘
│ │
└───────────────────────┬───────────────────────┘
│
┌─────────────▼─────────────┐
│ 现代 Web 应用数据通信 │
└───────────────────────────┘
🔑 总结关系:
- AJAX 是思想,XHR/Fetch 是实现该思想的原生工具
- Axios 是对 XHR(浏览器端)的高级封装
- Fetch 是浏览器提供的、与 XHR 并列的新一代原生 API
三、XMLHttpRequest 详解
3.1 基本使用流程
ini
const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/users', true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText);
}
};
xhr.send();
3.2 核心属性
| 属性 | 说明 |
|---|---|
readyState |
请求状态(0--4) |
status / statusText |
HTTP 状态码与描述 |
responseText |
字符串响应体 |
response |
根据 responseType 解析后的数据 |
responseType |
响应类型(json、blob、arraybuffer 等) |
3.3 事件模型
-
传统方式 :
onreadystatechange(需手动判断readyState) -
现代方式(推荐):
onload:请求完成onerror:网络错误ontimeout:超时onabort:被中止
3.4 高级功能
- 超时控制 :
xhr.timeout = 5000 - 跨域凭据 :
xhr.withCredentials = true - 上传进度 :
xhr.upload.onprogress - 中止请求 :
xhr.abort()
3.5 实际应用场景
文件上传(带进度)
ini
function uploadFile(file) {
const formData = new FormData();
formData.append('file', file);
const xhr = new XMLHttpRequest();
xhr.open('POST', '/upload');
xhr.upload.onprogress = (e) => {
if (e.lengthComputable) {
const percent = (e.loaded / e.total) * 100;
updateProgress(percent);
}
};
xhr.onload = () => {
if (xhr.status === 200) showSuccess();
};
xhr.send(formData);
}
四、Fetch API 原理深度解析
4.1 核心设计:基于 Stream 的请求/响应模型
Fetch 的核心是两个构造函数:
Request:表示 HTTP 请求Response:表示 HTTP 响应
两者都实现了 Body mixin,包含可读流(ReadableStream):
javascript
fetch('/api/data')
.then(response => {
console.log(response.body instanceof ReadableStream); // true
return response.json(); // 内部读取 body 流并解析
});
💡 关键机制 :
Fetch 将响应体视为流(Stream) ,支持边下载边处理,适合大文件或实时数据。
4.2 执行流程(浏览器内部)
以 Chromium 为例:
- 调用
fetch(url)→ 创建Request对象 - 浏览器主线程 → 网络服务线程(Network Service)
- 网络线程发起 HTTP 请求(复用连接池、DNS 缓存等)
- 收到响应头 → 立即 resolve Promise(返回
Response对象) - 响应体通过 ReadableStream 逐步传输到 JS 主线程
- 调用
.json()/.text()等方法 → 消费流并解析
4.3 与 XHR 的关键差异
| 特性 | XHR | Fetch |
|---|---|---|
| 错误处理 | 网络错误 → onerror;HTTP 错误(404/500)→ onload |
仅网络错误 reject ;HTTP 错误仍 resolve(需手动检查 response.ok) |
| Cookie 发送 | 同域自动发送 | 需显式设置 credentials: 'same-origin' |
| 取消请求 | xhr.abort() |
AbortController |
| 上传进度 | 原生 upload.onprogress |
不支持 (需自定义 ReadableStream,复杂) |
| 超时控制 | xhr.timeout |
需配合 AbortController + setTimeout |
错误处理对比示例:
javascript
// Fetch:HTTP 404 仍 resolve
fetch('/not-found')
.then(res => {
if (!res.ok) { // 必须手动检查
throw new Error(`HTTP ${res.status}`);
}
})
.catch(err => {
// 只有网络断开才会进入这里
});
4.4 Fetch 的局限性与解决方案
问题 1:无法监控下载进度
解决方案:手动读取流并计算进度:
ini
const response = await fetch('/large-file');
const contentLength = +response.headers.get('Content-Length');
let loaded = 0;
const reader = response.body.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
loaded += value.length;
const progress = (loaded / contentLength) * 100;
updateProgress(progress);
}
问题 2:无内置超时
解决方案 :结合 AbortController:
scss
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
fetch('/api/data', { signal: controller.signal })
.finally(() => clearTimeout(timeoutId));
五、Vue 3 中的网络通信实践
虽然 Vue 本身不强制使用特定 HTTP 客户端,但其组合式 API 与现代请求库天然契合。
5.1 使用 Axios(推荐用于复杂项目)
ini
// composables/useApi.js
import axios from 'axios';
const api = axios.create({
baseURL: '/api',
timeout: 10000,
withCredentials: true
});
// 请求拦截器
api.interceptors.request.use(config => {
const token = localStorage.getItem('token');
if (token) config.headers.Authorization = `Bearer ${token}`;
return config;
});
// 响应拦截器
api.interceptors.response.use(
response => response.data,
error => {
if (error.response?.status === 401) {
// 处理未授权
router.push('/login');
}
return Promise.reject(error);
}
);
export default api;
xml
<!-- 在组件中使用 -->
<script setup>
import { ref } from 'vue';
import api from '@/composables/useApi';
const users = ref([]);
const loading = ref(false);
const fetchUsers = async () => {
loading.value = true;
try {
users.value = await api.get('/users');
} finally {
loading.value = false;
}
};
fetchUsers();
</script>
5.2 使用 Fetch(轻量级项目)
javascript
// utils/request.js
async function request(url, options = {}) {
const config = {
credentials: 'include',
...options,
headers: {
'Content-Type': 'application/json',
...options.headers
}
};
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000);
try {
const response = await fetch(url, {
...config,
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
} catch (error) {
clearTimeout(timeoutId);
throw error;
}
}
export { request };
5.3 封装为 Composable(最佳实践)
ini
// composables/useFetch.js
import { ref } from 'vue';
export function useFetch(url) {
const data = ref(null);
const loading = ref(false);
const error = ref(null);
const execute = async () => {
loading.value = true;
error.value = null;
try {
const res = await fetch(url);
if (!res.ok) throw new Error(res.statusText);
data.value = await res.json();
} catch (err) {
error.value = err;
} finally {
loading.value = false;
}
};
return { data, loading, error, execute };
}
xml
<script setup>
import { useFetch } from '@/composables/useFetch';
const { data: users, loading, execute } = useFetch('/api/users');
execute();
</script>
六、如何选择?------ 使用场景建议
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 新项目(现代浏览器) | fetch() + 工具函数封装 |
原生支持,无依赖,符合标准 |
| 需要上传/下载进度 | XMLHttpRequest 或 Axios |
原生支持 onprogress,简单可靠 |
| 复杂拦截、转换、兼容 Node.js | Axios |
功能全面,生态成熟 |
| 维护旧项目(IE11+) | XMLHttpRequest 或 Axios(带 polyfill) |
最大兼容性 |
| 轻量级应用,避免打包体积 | fetch() |
无需引入第三方库 |
| Vue 3 项目 | Axios(复杂) 或 Fetch + Composable(简单) | 与组合式 API 完美契合 |
📌 现代最佳实践:
- 优先使用
fetch()或 Axios- 将网络逻辑封装为 Composable,实现逻辑复用
- 避免直接使用裸 XHR(除非特殊需求)
七、安全与性能注意事项
7.1 安全
- XSS 防护 :永远不要将响应直接插入
innerHTML - CSRF 防护:使用 anti-CSRF token,重要操作用非 GET 方法
- CORS 策略 :服务器严格限制
Access-Control-Allow-Origin - 敏感数据:使用 HTTPS,避免客户端存储密码/token
7.2 性能
- 缓存策略 :合理设置
Cache-Control头 - 请求合并:避免频繁小请求
- 懒加载:非关键数据延迟请求
- 取消冗余请求:组件销毁时中止未完成的请求
javascript
// Vue 3 中取消请求
import { onUnmounted } from 'vue';
export function useFetch(url) {
const controller = new AbortController();
onUnmounted(() => {
controller.abort(); // 组件卸载时取消请求
});
const execute = () => {
return fetch(url, { signal: controller.signal });
};
return { execute };
}
结语
理解 XHR、AJAX、Axios 与 Fetch 的关系,本质上是理解 Web 异步通信技术的演进史:
- AJAX 提出了"异步更新"的思想
- XHR 提供了首个标准化实现
- Axios 在 XHR 基础上构建了开发者友好的抽象
- Fetch 则代表了浏览器厂商对下一代网络 API 的重新设计
作为开发者,我们不必拘泥于某一种工具,而应根据项目需求、浏览器支持和功能复杂度做出合理选择。但无论使用哪种方式,其背后的核心原理------HTTP 协议、CORS 安全模型、异步编程范式------始终不变。
在 Vue 3 的组合式 API 时代,将网络逻辑封装为可复用的 Composable,不仅能提升代码可维护性,更能充分发挥现代 JavaScript 的表达力。掌握这些底层逻辑,才能在技术变迁中游刃有余,构建出高性能、高安全性的现代 Web 应用。