网络请求是前端开发中的核心概念,从XMLHttpRequest到Fetch API再到Ajax,这些术语确实需要系统了解。本文将用通俗易懂的语言解释这些概念,帮助你全面理解JavaScript中的网络请求。
网络请求是什么?为什么我们需要它?
想象一下,你在美团上点了一份外卖。你需要告诉商家你想要什么(请求),然后商家会给你回复(响应)。网页中的HTTP请求也是类似的过程!
简单来说,当你:
- 打开一个网页
- 点击"加载更多"按钮
- 提交一个表单
- 在网页上发送消息
这些操作背后,浏览器都在和服务器进行"对话",这就是HTTP请求的本质。
这种"对话"包括:
- 你说什么(请求方法:GET、POST等)
- 你对谁说(URL地址)
- 你怎么说(请求头)
- 说什么内容(请求体)
然后服务器会回应:
- 听到了没(状态码:200成功,404没找到等)
- 回应的方式(响应头)
- 回应 的内容(响应体)
JavaScript中的两大网络请求主角
在JavaScript中,我们有两种主要的方式发送网络请求:
- XMLHttpRequest(简称XHR):老前辈,资历深但脾气有点怪
- Fetch API:新生代,时尚灵活但有时候太年轻气盛
我们先来看看这位老前辈。
XMLHttpRequest:老当益壮的网络请求大爷
XMLHttpRequest虽然名字里有"XML",但它其实什么数据都能处理。这就像一个叫"面条专家"的厨师,其实煲汤和炒菜也很拿手。
基本使用
// 创建一个XHR对象(相当于拿起电话准备打)
const xhr = new XMLHttpRequest();
// 设置请求(拨号)
xhr.open('GET', 'https://api.example.com/data', true); // true表示异步,就像你打电话后可以做别的事
// 设置接收数据的格式
xhr.responseType = 'json'; // 可以是'text'、'json'、'blob'等
// 监听状态变化(等待对方接电话并说话)
xhr.onreadystatechange = function() {
// readyState就像电话的状态:
// 0: 刚拿起电话
// 1: 开始拨号
// 2: 对方接听了
// 3: 对方正在说话
// 4: 通话结束
if (xhr.readyState === 4) { // 通话结束
if (xhr.status === 200) { // 对方好好回答了
console.log('收到数据啦:', xhr.response);
} else {
console.error('哎呀,出错了:', xhr.status);
}
}
};
// 更简单的方式监听(我只关心通话结束)
xhr.onload = function() {
if (xhr.status === 200) {
console.log('收到数据啦:', xhr.response);
} else {
console.error('哎呀,出错了:', xhr.status);
}
};
// 处理出错情况(电话线路问题)
xhr.onerror = function() {
console.error('网络出问题了,可能是你的WiFi断了?');
};
// 发送请求(按下拨号键)
xhr.send(); // GET请求是空的,POST会带上数据
老实说,XHR的代码看起来有点复杂,这也是为什么后来大家都更喜欢用Fetch API或Axios。
发送POST请求
如果GET请求是"问问题",那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) { // 201表示"我已经创建了你要的东西"
console.log('用户创建成功啦:', xhr.response);
} else {
console.error('创建失败了,可能是表单填错了?', xhr.status);
}
};
// 准备要发送的数据
const data = {
name: '张三',
email: 'zhangsan@example.com'
};
// 发送数据(一定要先转成JSON字符串!)
xhr.send(JSON.stringify(data));
我第一次用POST请求时,就忘了把对象转成JSON字符串,调试了半天才发现问题。这是很多新手都会踩的坑!
上传文件
上传文件时,我们需要用到FormData 对象:
const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://api.example.com/upload', true);
// 监控上传进度(这是XHR的一个很酷的特性!)
xhr.upload.onprogress = function(e) {
if (e.lengthComputable) {
const percentComplete = (e.loaded / e.total) * 100;
console.log('已经上传了:', percentComplete.toFixed(2) + '%');
// 这里可以更新进度条UI
}
};
xhr.onload = function() {
if (xhr.status === 200) {
console.log('文件上传成功!', xhr.response);
} else {
console.error('上传失败了,文件太大了?', xhr.status);
}
};
// 创建FormData对象(相当于一个虚拟表单)
const formData = new FormData();
// 假设页面上有个文件选择器
const fileInput = document.getElementById('fileInput');
formData.append('file', fileInput.files[0]);
formData.append('username', '张三'); // 可以添加额外信息
// 发送FormData
xhr.send(formData);
我记得有一次做文件上传功能,用户反馈上传大文件时没有进度提示,体验很差。加上这个进度监控后,用户满意度立刻提高了!
设置超时和取消请求
有时候服务器响应太慢,我们需要设置超时:
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data', true);
// 设置5秒超时(如果5秒内没回应就放弃)
xhr.timeout = 5000; // 毫秒
// 超时处理
xhr.ontimeout = function() {
console.error('服务器太慢了,等不及了!');
// 这里可以提示用户稍后再试
};
xhr.send();
// 如果用户突然不想等了,我们可以取消请求
// xhr.abort(); // 挂断电话
Fetch API:新时代的网络请求小鲜肉
Fetch API是现代浏览器提供的更简洁的API,它基于Promise,写起来更加优雅。
基本使用
// 基本GET请求,简单多了吧?
fetch('https://api.example.com/data')
.then(response => {
// 注意这个坑:fetch不会自动因为HTTP错误状态而失败!
if (!response.ok) {
throw new Error(`出错了: ${response.status}`);
}
// 解析JSON
return response.json();
})
.then(data => {
console.log('获取的数据:', data);
})
.catch(error => {
console.error('获取数据失败:', error);
});
注意:Fetch API有个奇怪的特性 - 即使服务器返回404或500错误,它也不会自动reject Promise。你必须手动检查response.ok。这个"特性"让我第一次使用时很困惑!
发送POST请求
// 准备数据
const data = {
name: '张三',
email: 'zhangsan@example.com'
};
fetch('https://api.example.com/users', {
method: 'POST', // 请求方法
headers: {
'Content-Type': 'application/json', // 告诉服务器我们发送的是JSON
},
body: JSON.stringify(data) // 一样要转成JSON字符串
})
.then(response => {
if (!response.ok) {
throw new Error(`出错了: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('创建成功:', data);
})
.catch(error => {
console.error('创建失败:', error);
});