JS 发送 HTTP 请求的四种方式:从基础到现代实践
前言
在 Web 开发中,前端与后端的数据交互是核心环节。JavaScript 提供了多种发送 HTTP 请求的方式,从早期的 XMLHttpRequest 到现代的 Fetch API,再到第三方库和语法层面的支持。理解它们之间的区别和适用场景,能够帮助你在实际项目中做出更合适的选择。
本文会依次介绍四种主流方式,并附上可以直接运行的代码示例。
一、XMLHttpRequest:经典的基石
XMLHttpRequest(简称 XHR)是浏览器最早提供的异步请求接口。尽管名字里带有 XML,但它支持所有类型的数据格式,比如 JSON、HTML、纯文本等。它的核心作用是让页面在不整体刷新的情况下,向服务器发送请求并获取数据,这正是 Ajax 技术的核心。
基本用法
下面是一个完整的异步 GET 请求示例:
// 1. 创建一个 XHR 实例
const xhr = new XMLHttpRequest();
// 2. 配置请求:方法、地址、是否异步
xhr.open('GET', 'https://api.example.com/data', true);
// 3. 监听加载完成事件
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status < 300) {
// 请求成功
const data = JSON.parse(xhr.responseText);
console.log('获取到的数据:', data);
} else {
// 服务器返回了错误状态码
console.error('请求失败,状态码:', xhr.status);
}
};
// 4. 监听网络错误
xhr.onerror = function () {
console.error('网络连接出现问题');
};
// 5. 发送请求
xhr.send();同步请求的注意事项
XHR 支持同步模式,只需将 open() 的第三个参数设为 false。但同步请求会阻塞主线程,在请求完成之前用户无法进行任何操作,体验很差,现代浏览器已在主线程中逐步弃用这种做法。
// 不推荐:同步请求
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data', false);
xhr.send();
if (xhr.status === 200) {
console.log(xhr.responseText);
}除非在 Worker 线程中有特殊需求,否则应始终使用异步模式。
补充:sendBeacon 方法
还有一个与 XHR 相关但独立的 API 值得一提:navigator.sendBeacon()。它专门用于在页面即将卸载时,向服务器发送少量数据,比如埋点统计或用户行为的最后记录。
// 页面关闭前发送统计数据
window.addEventListener('unload', function () {
const data = { event: 'page_close', timestamp: Date.now() };
navigator.sendBeacon('/api/analytics', JSON.stringify(data));
});它的优势在于:即使页面正在关闭,浏览器也会保证请求发出,不会因为页面卸载而中断。
二、Fetch API:现代的 Promise 风格
Fetch 是 ES6 引入的新接口,基于 Promise 设计,语法更简洁、逻辑更清晰。它被认为是 XMLHttpRequest 的现代化替代方案。
基本 GET 请求
fetch('https://api.example.com/data')
.then(function (response) {
// 先检查响应是否成功
if (!response.ok) {
throw new Error('网络响应异常,状态码: ' + response.status);
}
// 将响应体解析为 JSON
return response.json();
})
.then(function (data) {
console.log('获取到的数据:', data);
})
.catch(function (error) {
console.error('请求出错:', error.message);
});一个重要的认知:错误处理
Fetch 有一个容易误解的地方:当服务器返回 404 或 500 这样的错误状态码时,Fetch 返回的 Promise 并不会进入 reject 状态,而是会正常 resolve,只是 response.ok 会变为 false。只有在网络故障或请求被拦截时,Promise 才会 reject。
因此,自己动手检查 response.ok 是一个好习惯,上面已经展示了具体写法。
POST 请求示例
function postData(url, data) {
return fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(function (response) {
if (!response.ok) {
throw new Error('请求失败,状态码: ' + response.status);
}
return response.json();
});
}
// 使用
postData('https://api.example.com/submit', {
name: '张三',
age: 28
})
.then(function (result) {
console.log('提交结果:', result);
})
.catch(function (error) {
console.error('提交失败:', error.message);
});关于跨域和 Cookie
默认情况下,Fetch 不会发送跨站 Cookie。如果需要携带身份凭证,可以设置 credentials 选项:
fetch('https://api.example.com/user', {
credentials: 'include' // 始终发送 cookie
});三、Axios:功能更强的第三方库
在实际项目中,很多开发者会选择 Axios 这个 HTTP 客户端库。它封装了 XHR,提供了更友好的 API 和更丰富的功能。
安装方式
你可以通过 CDN 直接引入,也可以通过 npm 安装:
npm install axios发送 GET 请求
// 使用 CDN 引入后,axios 会挂载到全局
axios.get('https://api.example.com/data')
.then(function (response) {
// 响应数据在 response.data 中,无需手动解析 JSON
console.log('数据:', response.data);
})
.catch(function (error) {
// 状态码非 2xx 时会自动进入 catch
console.error('请求出错:', error.message);
});发送 POST 请求
axios.post('https://api.example.com/submit', {
name: '张三',
age: 28
})
.then(function (response) {
console.log('提交成功:', response.data);
})
.catch(function (error) {
console.error('提交失败:', error.message);
});Axios 的优势
与原生 Fetch 相比,Axios 有几个实用特性:
自动转换 JSON:请求数据会自动转为 JSON,响应数据会自动解析,不用手动调用 JSON.stringify 和 response.json()。
错误处理更直观:所有非 2xx 的状态码都会让 Promise 进入 reject,符合多数开发者的直觉。
请求和响应拦截器:可以在请求发出前或响应到达后统一处理,比如添加 Token、记录日志、处理全局错误。
取消请求:通过 CancelToken 或 AbortController 可以主动取消正在进行的请求。
超时配置:直接设置 timeout 属性即可,Fetch 原生不支持。
// 请求拦截器示例:统一添加 Token
axios.interceptors.request.use(function (config) {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = 'Bearer ' + token;
}
return config;
});
// 响应拦截器示例:统一处理错误
axios.interceptors.response.use(
function (response) {
return response;
},
function (error) {
if (error.response && error.response.status === 401) {
// 跳转到登录页
window.location.href = '/login';
}
return Promise.reject(error);
}
);四、Async/Await:让异步代码像同步一样
Async/Await 是 ES2017 引入的语法,它建立在 Promise 之上,让异步代码的结构更接近同步代码,可读性更好。它与 Fetch 或 Axios 搭配使用,效果非常好。
基本语法
async 放在函数前面,表示该函数会返回一个 Promise。
await 放在返回 Promise 的表达式前面,会暂停函数执行,等待这个 Promise 完成,然后返回结果。
// 定义一个返回 Promise 的辅助函数
function delay(ms) {
return new Promise(function (resolve) {
setTimeout(resolve, ms);
});
}
// 使用 async/await
async function delayedMessage(value, ms) {
await delay(ms);
console.log(value);
}
delayedMessage('你好,世界', 2000);
// 2 秒后输出:你好,世界搭配 Fetch 使用
async function fetchUserData(userId) {
try {
const response = await fetch('https://api.example.com/users/' + userId);
if (!response.ok) {
throw new Error('获取用户信息失败,状态码: ' + response.status);
}
const userData = await response.json();
console.log('用户数据:', userData);
return userData;
} catch (error) {
console.error('发生错误:', error.message);
// 可以根据需要做进一步处理
}
}
// 调用
fetchUserData(123);搭配 Axios 使用
async function submitForm() {
try {
const response = await axios.post('https://api.example.com/form', {
title: '申请单',
content: '这是申请内容'
});
console.log('提交成功:', response.data);
return response.data;
} catch (error) {
if (error.response) {
// 服务器有响应,但状态码不是 2xx
console.error('服务器错误:', error.response.status);
} else if (error.request) {
// 请求发出但没有收到响应
console.error('无响应,请检查网络');
} else {
console.error('请求配置出错:', error.message);
}
}
}错误处理的最佳实践
用 try...catch 包裹 await 表达式,可以统一捕获同步和异步的错误。这样无论错误来自网络、服务器还是代码逻辑,都能在一处处理。
五、四种方式的对比与选择
| 特性 | XMLHttpRequest | Fetch | Axios | Async/Await |
|---|---|---|---|---|
| 语法复杂度 | 高(回调嵌套) | 中(Promise 链) | 中(Promise 链) | 低(同步风格) |
| 错误处理 | 手动处理 | 需自行检查 ok | 自动抛出非 2xx 错误 | 用 try/catch 统一处理 |
| 自动 JSON 转换 | 否 | 否 | 是 | 取决于搭配的底层 |
| 请求超时 | 支持 | 不支持原生方法 | 支持 | 取决于搭配的底层 |
| 上传/下载进度 | 支持 | 不完全支持 | 支持 | 取决于搭配的底层 |
| 浏览器兼容性 | 全部 | 现代浏览器 | 全部(需引入库) | 现代浏览器 |
如何选择
如果你需要兼容非常老旧的浏览器,而且不想引入第三方库,XMLHttpRequest 是唯一选择。
如果你的项目是现代浏览器,而且不想增加额外的依赖,Fetch 足够应对大多数场景。
如果你的项目需要请求拦截、超时处理、上传进度等高级功能,或者你更看重开发效率,Axios 是很好的选择。
Async/Await 是一个语法层面的改进,不是独立方法。它需要搭配 Fetch 或 Axios 使用,作用是让代码更加清晰。建议任何支持 ES2017 的项目都采用这种写法。
写在最后
从 XMLHttpRequest 到 Fetch,再到 Axios 这类封装库,JS 发送 HTTP 请求的方式在不断演进。每一种方案都有它的历史定位和适用场景。掌握它们,能让你在面对不同项目需求时,快速选到最合适的工具。
希望这篇文章对你的学习和工作有所帮助。
本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!