在现代Web开发中,Ajax、XMLHttpRequest(XHR)、Promise、Fetch和Axios,这几个词虽然都跟“前端发送网络请求”相关,但它们的层次和定位截然不同。
1. 一句话概览关系
Ajax是一种技术概念/模式,不是具体的API或库。(目的:无刷新更新页面)
XHR和Fetch是浏览器提供的原生底层工具。(手段:怎么把请求发出去)
Promise是 JavaScript 处理异步逻辑的语法规范。(工具:怎么管理回调)
Axios是基于Promise并封装了XHR/Fetch的第三方库。(目的:让发请求更舒服)
2. 详细拆解与举例
① Ajax (Asynchronous JavaScript And XML)
本质:不是具体的 API,而是一套技术组合拳。2005 年 Jesse James Garrett 提出,指利用
XMLHttpRequest对象在后台与服务器交换数据,并通过 DOM 操作局部更新页面。核心特点:异步执行+无刷新。通常依赖XMLHttpRequest对象(或Fetch API)来实现异步通信。它允许网页在不重新加载的情况下,与服务器交换数据并更新部分网页内容。
现状:虽然名字带 XML,但现在 99% 的情况是用 JSON 格式。
② XHR (XMLHttpRequest)
本质:浏览器提供的原生构造函数,是实现 Ajax 最古老的底层 API。
写法特征:基于事件监听(
onreadystatechange或onload),容易陷入回调地狱(Callback Hell)。
构造函数
const xhr = new XMLHttpRequest();
创建一个 XHR 实例,在调用任何其他方法前必须先实例化
主要属性
| 属性 | 类型 | 说明 |
|---|---|---|
readyState | 只读 number | 请求状态:0=UNSENT(未调用 open),1=OPENED(已调用 open),2=HEADERS_RECEIVED(已接收响应头),3=LOADING(接收响应体中),4=DONE(完成)-1 |
status | 只读 number | HTTP 响应状态码(200、404、500 等) |
statusText | 只读 string | HTTP 状态文本("OK"、"Not Found" 等) |
response | 只读 any | 响应体,类型由responseType决定 |
responseText | 只读 string | 字符串格式的响应体(当responseType为空或 "text" 时) |
responseXML | 只读 Document | 以 XML 文档形式解析的响应 |
responseType | string | 指定响应数据类型:""(文本)、"arraybuffer"、"blob"、"document"、"json"、"text" |
timeout | number | 超时时间(毫秒),超时后触发ontimeout事件 |
withCredentials | boolean | 跨域请求是否携带 Cookie,默认 false |
upload | 只读 XMLHttpRequestUpload | 上传对象,可监听上传进度事件 |
主要方法
| 方法 | 说明 |
|---|---|
open(method, url, async, user, password) | 初始化请求。async默认为 true(异步),设为 false 会阻塞主线程(不推荐)-3 |
send(body) | 发送请求。GET/HEAD 请求通常不传 body |
setRequestHeader(name, value) | 设置请求头,必须在open()之后、send()之前调用 |
getResponseHeader(name) | 获取指定响应头的值 |
getAllResponseHeaders() | 获取所有响应头(CRLF 分隔的字符串) |
abort() | 取消请求,触发onabort事件 |
overrideMimeType(mime) | 强制指定响应 MIME 类型 |
主要事件
| 事件 | 说明 |
|---|---|
onreadystatechange | 每当readyState变化时触发 |
onload | 请求成功完成时触发(等价于readyState === 4且状态成功)-6 |
onerror | 网络层面错误(DNS 失败、断网等)时触发 |
onprogress | 接收数据时周期性触发,可获取下载进度 |
onloadstart/onloadend | 请求开始/结束时触发 |
ontimeout | 超时时触发 |
onabort | 调用abort()取消请求时触发 |
upload.onprogress | 上传进度事件,用于监控上传进度 |
代码示例:
const xhr = new XMLHttpRequest(); xhr.open('GET', 'https://api.example.com/data'); // 必须监听状态变化 xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status === 200) { console.log('成功', JSON.parse(xhr.responseText)); } else { console.log('失败'); } } }; xhr.send();// XHR 详细示例 function xhrExample() { // 创建 XMLHttpRequest 对象 const xhr = new XMLHttpRequest(); // 配置请求 xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1', true); // 设置请求头(可选) xhr.setRequestHeader('Content-Type', 'application/json'); // 监听状态变化 xhr.onreadystatechange = function() { // readyState: 0=未初始化, 1=正在打开, 2=已发送, 3=接收中, 4=完成 if (xhr.readyState === 4) { if (xhr.status === 200) { // 成功 document.getElementById('xhrResult').innerHTML = '<h3>XHR 成功</h3><pre>' + JSON.stringify(JSON.parse(xhr.responseText), null, 2) + '</pre>'; document.getElementById('xhrResult').style.display = 'block'; } else { // 失败 document.getElementById('xhrResult').innerHTML = '<h3>XHR 失败</h3><p>状态码: ' + xhr.status + '</p>'; document.getElementById('xhrResult').style.display = 'block'; } } }; // 错误处理 xhr.onerror = function() { document.getElementById('xhrResult').innerHTML = '<h3>XHR 网络错误</h3>'; document.getElementById('xhrResult').style.display = 'block'; }; // 发送请求 xhr.send(); }//基础 GET 请求: // 1. 创建实例 const xhr = new XMLHttpRequest(); // 2. 初始化请求(GET + 异步) xhr.open('GET', 'https://api.example.com/users/1', true); // 3. 设置响应类型(自动解析 JSON) xhr.responseType = 'json'; // 4. 设置超时 xhr.timeout = 5000; // 5. 监听事件 xhr.onload = function() { // onload 只在请求成功时触发 if (xhr.status >= 200 && xhr.status < 300) { console.log('请求成功:', xhr.response); } else { console.error('HTTP 错误:', xhr.status); } }; xhr.onerror = function() { console.error('网络错误或跨域问题'); }; xhr.ontimeout = function() { console.error('请求超时'); }; // 6. 发送请求 xhr.send();//POST 请求(发送 JSON): const xhr = new XMLHttpRequest(); xhr.open('POST', 'https://api.example.com/users', true); // 设置请求头 xhr.setRequestHeader('Content-Type', 'application/json'); xhr.onload = function() { if (xhr.status >= 200 && xhr.status < 300) { console.log('创建成功:', JSON.parse(xhr.responseText)); } }; const payload = { name: '张三', age: 25 }; xhr.send(JSON.stringify(payload));//上传进度监控: const xhr = new XMLHttpRequest(); xhr.open('POST', 'https://api.example.com/upload', true); const formData = new FormData(); formData.append('file', fileInput.files[0]); // 监听上传进度 xhr.upload.onprogress = function(event) { if (event.lengthComputable) { const percent = (event.loaded / event.total) * 100; console.log(`上传进度: ${percent.toFixed(2)}%`); } }; xhr.onload = function() { console.log('上传完成'); }; xhr.send(formData);XHR 工作流程
创建对象 → open() 配置请求 → send() 发送请求 → 监听状态变化 → 处理响应
readyState 含义
| 值 | 含义 |
|---|---|
| 0 | 初始化 |
| 1 | open 已调用 |
| 2 | 收到响应头 |
| 3 | 接收数据中 |
| 4 | 请求完成 |
③ Promise
本质:ES6 引入的语法标准,用于解决异步回调嵌套问题。
关键点:Promise 本身不是用来发请求的,而是用来包装异步操作的容器。Fetch 和 Axios 都使用了 Promise 作为返回值。
代码示例(用 Promise 包装 XHR):
function request(url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.onload = () => resolve(xhr.response); xhr.onerror = () => reject(xhr.statusText); xhr.send(); }); } // 使用 then/catch 链式调用,告别回调地狱 request('/api').then(data => console.log(data));//Promise 示例(模拟请求) const fetchData = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ id: 1, name: 'Qwen' }); }, 1000); }); }; fetchData().then(data => console.log(data));Promise 三种状态
| 状态 | 含义 |
|---|---|
| pending | 进行中 |
| fulfilled | 成功 |
| rejected | 失败 |
④ Fetch API
本质:浏览器提供的第二代原生请求 API,替代 XHR。
核心特征:
基于 Promise,语法简洁现代。
默认不携带 Cookie(需设置
credentials: 'include')。只会在网络故障时 reject,对于 404、500 这类 HTTP 错误状态码,Fetch 依然会认为请求成功(resolve),需要手动判断
response.ok。
fetch() 函数:
fetch(resource, init)
resource:请求 URL 或 Request 对象
init:可选配置对象,包含以下常用字段:
| 配置项 | 类型 | 说明 |
|---|---|---|
method | string | HTTP 方法:GET、POST、PUT、DELETE 等 |
headers | object/Headers | 请求头对象,如{ 'Content-Type': 'application/json' } |
body | string/FormData/Blob 等 | 请求体,GET/HEAD 不能有 body |
mode | string | 跨域模式:"cors"(默认)、"no-cors"、"same-origin" |
credentials | string | 凭证策略:"omit"(默认,不带 Cookie)、"same-origin"(同域带)、"include"(始终带)-16 |
cache | string | 缓存策略:"default"、"no-store"、"reload" 等 |
redirect | string | 重定向处理:"follow"(默认)、"error"、"manual" |
signal | AbortSignal | 用于取消请求 |
Response 对象
fetch()返回的 Promise 会 resolve 为 Response 对象,包含以下常用属性和方法:
| 属性/方法 | 说明 |
|---|---|
response.ok | boolean,状态码 200-299 时为 true-15 |
response.status | HTTP 状态码 |
response.statusText | 状态文本 |
response.headers | Headers 对象 |
response.json() | 解析 JSON,返回 Promise |
response.text() | 读取为文本,返回 Promise |
response.blob() | 读取为 Blob,返回 Promise |
response.formData() | 读取为 FormData,返回 Promise |
response.arrayBuffer() | 读取为 ArrayBuffer,返回 Promise |
response.body | ReadableStream,可用于流式读取 |
代码示例:
fetch('https://api.example.com/data', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: 1 }) }) .then(response => { // 注意:必须手动检查 status! if (!response.ok) { throw new Error('HTTP error ' + response.status); } return response.json(); // 这也是个 Promise }) .then(data => console.log('Fetch 结果', data)) .catch(err => console.log('网络挂了或逻辑错误', err));// Fetch 详细示例 async function fetchExample() { try { // 发起请求 const response = await fetch('https://jsonplaceholder.typicode.com/posts/1', { method: 'GET', headers: { 'Content-Type': 'application/json', // 'Authorization': 'Bearer your-token' // 如需要认证 }, // credentials: 'include', // 包含cookies }); // 检查响应状态 if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } // 解析 JSON 数据 const data = await response.json(); document.getElementById('fetchResult').innerHTML = '<h3>Fetch 成功</h3><pre>' + JSON.stringify(data, null, 2) + '</pre>'; document.getElementById('fetchResult').style.display = 'block'; } catch (error) { document.getElementById('fetchResult').innerHTML = '<h3>Fetch 错误</h3><p>错误信息: ' + error.message + '</p>'; document.getElementById('fetchResult').style.display = 'block'; } }//基础 GET 请求: async function getUser(id) { try { const response = await fetch(`https://api.example.com/users/${id}`); // Fetch 不会因 HTTP 错误而 reject,需手动检查 if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); console.log('用户数据:', data); return data; } catch (error) { console.error('请求失败:', error.message); } }//POST 请求(发送 JSON) async function createUser(userData) { try { const response = await fetch('https://api.example.com/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(userData) }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || '创建失败'); } const newUser = await response.json(); console.log('创建成功:', newUser); return newUser; } catch (error) { console.error('请求失败:', error); } } createUser({ name: '李四', email: 'lisi@example.com' });//带 Cookie 的跨域请求: async function fetchWithCredentials(url) { const response = await fetch(url, { credentials: 'include' // 跨域请求也携带 Cookie }); return response.json(); }//超时控制(使用 AbortController): function fetchWithTimeout(url, timeout = 8000) { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); return fetch(url, { signal: controller.signal }) .finally(() => clearTimeout(timeoutId)); } // 使用 try { const response = await fetchWithTimeout('https://api.example.com/slow-endpoint', 5000); const data = await response.json(); } catch (error) { if (error.name === 'AbortError') { console.error('请求超时'); } else { console.error('其他错误:', error); } }⑤ Axios
本质:第三方 HTTP 库。在浏览器端它底层调用的是XHR,在 Node.js 端调用的是http 模块。
为什么用 Axios 而不是直接用 Fetch?
自动 JSON 转换:不用写两次
then或手动JSON.parse。请求/响应拦截器:可以在请求前统一加 Token,或在响应后统一处理报错。
取消请求:提供了
CancelToken或AbortController的简单封装。超时设置:Fetch 本身不支持超时配置,Axios 可以直接配
timeout。
基本请求方法:
axios(config) // 通用请求 axios.get(url, config) axios.post(url, data, config) axios.put(url, data, config) axios.patch(url, data, config) axios.delete(url, config) axios.head(url, config) axios.options(url, config) axios.request(config) // 等同于 axios(config)请求配置(config)常用字段:
| 配置项 | 类型 | 说明 |
|---|---|---|
url | string | 请求 URL |
method | string | 请求方法(默认 GET) |
baseURL | string | 基础 URL,会与url拼接 |
headers | object | 自定义请求头 |
params | object | URL 查询参数,自动序列化 |
data | any | 请求体数据,对象会自动序列化为 JSON |
timeout | number | 超时时间(毫秒) |
withCredentials | boolean | 跨域请求是否携带 Cookie |
responseType | string | 响应数据类型:"json"(默认)、"blob"、"text" 等 |
cancelToken | CancelToken | 用于取消请求(已废弃,推荐使用signal) |
signal | AbortSignal | 取消请求(与 Fetch 兼容) |
代码示例:
// 1. 配置拦截器(全局操作) axios.interceptors.request.use(config => { config.headers.Authorization = `Bearer token`; return config; }); // 2. 发送请求 axios.post('/api/data', { name: 'DeepSeek' }) .then(response => { // 这里的 response.data 已经是解析好的对象,且 4xx/5xx 会自动进 catch console.log(response.data); }) .catch(error => { // Axios 自动处理了 HTTP 错误状态码 console.log('请求失败', error.response?.status); });// Axios 详细示例 async function axiosExample() { try { // 发起请求 const response = await axios.get('https://jsonplaceholder.typicode.com/posts/1', { headers: { 'Content-Type': 'application/json', // 'Authorization': 'Bearer your-token' // 如需要认证 }, timeout: 5000, // 超时时间 }); // 直接访问数据(Axios自动解析JSON) const data = response.data; const status = response.status; const headers = response.headers; document.getElementById('axiosResult').innerHTML = '<h3>Axios 成功</h3>' + '<p>状态码: ' + status + '</p>' + '<pre>' + JSON.stringify(data, null, 2) + '</pre>'; document.getElementById('axiosResult').style.display = 'block'; } catch (error) { let errorMsg = '未知错误'; if (error.response) { // 服务器响应了错误状态码 errorMsg = `服务器错误: ${error.response.status} - ${error.response.statusText}`; } else if (error.request) { // 请求已发出但没有收到响应 errorMsg = '网络错误或请求超时'; } else { // 其他错误 errorMsg = error.message; } document.getElementById('axiosResult').innerHTML = '<h3>Axios 错误</h3><p>错误信息: ' + errorMsg + '</p>'; document.getElementById('axiosResult').style.display = 'block'; } }import axios from 'axios'; // 使用 AbortController(推荐,与 Fetch 统一) const controller = new AbortController(); axios.get('/api/data', { signal: controller.signal }) .then(response => console.log(response)) .catch(error => { if (axios.isCancel(error)) { console.log('请求已取消'); } }); // 取消请求 controller.abort();3. 核心区别对比表
| 维度 | XHR | Fetch | Axios |
|---|---|---|---|
| 类型 | 原生 API | 原生 API | 第三方库 (需安装) |
| 参数处理 | 需手动序列化/反序列化 JSON | 需两次then解析 JSON | 自动转换 JSON |
| 错误判定 | status200-299 算成功 | 仅网络错误 reject | HTTP 错误自动 reject |
| 超时控制 | 支持xhr.timeout | 原生不支持(需结合 AbortController) | 支持timeout参数 |
| 请求进度 | 支持onprogress(可做上传进度条) | 不支持上传进度 | 支持 (浏览器端) |
| 拦截器 | 无 | 无 | 强大 |
| 取消请求 | xhr.abort() | AbortController(稍繁琐) |
|
| 异步机制 | 事件监听(回调) (非Promise) | Promise + async/await | Promise + async/await |
| 跨域 Cookie | withCredentials = true | credentials: 'include' | withCredentials: true |
Ajax是指导思想,XHR是老旧实现,Fetch是现代原生实现但功能简陋,Promise是异步流程控制工具,而Axios是基于 Promise 对请求的工业级封装。
简单脚本或 PWA:直接用Fetch足矣(注意记得检查
res.ok)。复杂后台管理系统 / 全栈项目:Axios是事实标准,它的拦截器和自动 JSON 处理能极大减少样板代码。