3、Ajax与Fetch API:原生Ajax实现、Fetch API使用、跨域问题与CORS解决方案
说到前端数据交互,Ajax 和 Fetch 是绕不开的两个核心工具。我刚开始做前端那会儿,还是 jQuery 的 $.ajax 一统天下。后来原生 Fetch 出来,我第一时间就切过去了——不是因为跟风,而是它确实更干净、更符合现代 JavaScript 的写法。
这一节,咱们就把原生 Ajax、Fetch API 以及跨域问题彻底讲透。你想想看,搞懂了这些,以后跟后端对接基本不会卡壳。
3.1 原生Ajax实现
Ajax 全称是 Asynchronous JavaScript and XML。说白了,就是让页面在不刷新的情况下,偷偷跟服务器要数据。我早期做的一个后台管理系统,所有表格操作都靠 Ajax 完成,用户体验比传统表单提交好太多了。
原生 Ajax 的核心是 XMLHttpRequest 对象。别看它名字里带 XML,现在传 JSON 才是主流。
一个标准的 GET 请求长这样:
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/users', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
const data = JSON.parse(xhr.responseText);
console.log('拿到数据了:', data);
}
};
xhr.send();
这里有几个关键点要注意:
- readyState:0 到 4 五个状态,只有 4 表示请求完成
- status:HTTP 状态码,200 是成功,404 是找不到,500 是服务器挂了
- responseText:服务器返回的原始字符串
POST 请求稍微复杂一点,需要设置请求头:
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 === 201) {
console.log('创建成功:', xhr.responseText);
}
};
const body = JSON.stringify({ name: '张三', age: 28 });
xhr.send(body);
我个人习惯把 Ajax 封装成一个通用函数,避免每次重复写这些样板代码。比如这样:
function ajax(method, url, data) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(method, url, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.responseText));
} else {
reject(new Error(`请求失败: ${xhr.status}`));
}
};
xhr.onerror = () => reject(new Error('网络错误'));
xhr.send(data ? JSON.stringify(data) : null);
});
}
// 使用
ajax('GET', '/api/users').then(data => console.log(data));
3.2 Fetch API使用
Fetch API 是 XMLHttpRequest 的现代替代方案。它基于 Promise 设计,语法更简洁。我第一次用 Fetch 时,最大的感受就是——终于不用再手动处理 readyState 了。
一个简单的 Fetch GET 请求:
fetch('https://api.example.com/users')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('出错了:', error));
是不是清爽很多?但这里有个容易忽略的点:Fetch 只有在网络错误时才会 reject。HTTP 404、500 这些状态码,它依然会走 then,不会进 catch。
正确的做法是:
fetch('https://api.example.com/users')
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('请求失败:', error));
POST 请求的写法:
fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ name: '李四', age: 25 })
})
.then(response => response.json())
.then(data => console.log('创建成功:', data))
.catch(error => console.error('创建失败:', error));
我记得有一次,后端要求传 FormData 而不是 JSON。Fetch 处理起来也很方便:
const formData = new FormData();
formData.append('name', '王五');
formData.append('avatar', fileInput.files[0]);
fetch('/api/upload', {
method: 'POST',
body: formData // 注意:不要手动设置 Content-Type,浏览器会自动处理
})
.then(response => response.json())
.then(data => console.log(data));
3.3 跨域问题与CORS解决方案
跨域,是前端开发中绕不开的痛。你想想看,你的页面在 a.com,却想请求 b.com 的数据。浏览器出于安全考虑,默认是不允许的。这就是同源策略。
为什么会这样?说白了,如果没有同源策略,你打开一个恶意网站,它就能偷偷读取你的银行账户信息。浏览器为了保护你,才设了这个限制。
解决跨域问题,最主流的方式就是 CORS(跨域资源共享)。它需要后端配合,在响应头里加几个字段。
后端需要设置的响应头:
| 响应头 | 说明 | 示例 |
|---|---|---|
| Access-Control-Allow-Origin | 允许的源 | https://a.com 或 * |
| Access-Control-Allow-Methods | 允许的 HTTP 方法 | GET, POST, PUT, DELETE |
| Access-Control-Allow-Headers | 允许的自定义请求头 | Content-Type, Authorization |
| Access-Control-Allow-Credentials | 是否允许携带 Cookie | true |
比如后端用 Node.js Express 框架,可以这样配置:
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', 'https://a.com');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if (req.method === 'OPTIONS') {
return res.sendStatus(200); // 预检请求直接返回
}
next();
});
前端这边,如果请求需要携带 Cookie,需要额外设置:
fetch('https://b.com/api/data', {
method: 'GET',
credentials: 'include' // 携带 Cookie
})
.then(response => response.json())
.then(data => console.log(data));
同时,后端的 Access-Control-Allow-Origin 不能设为 *,必须指定具体的源。
除了 CORS,还有几种跨域方案:
- JSONP:利用 script 标签不受同源策略限制的特性。但只支持 GET 请求,安全性也差,现在基本不用了。
- 代理服务器:前端请求同源的后端,后端再去请求目标服务器。开发环境常用 webpack-dev-server 的 proxy 配置。
- postMessage:用于不同窗口或 iframe 之间的通信。
我个人最推荐 CORS,它是标准方案,功能全面,安全性也有保障。只要后端配合,基本能解决 99% 的跨域场景。
嗯,这一节的内容就到这里。Ajax 和 Fetch 的核心用法、跨域问题的解决方案,你都掌握了吗?下一节我们会聊更进阶的数据交互模式,敬请期待。