一、先搞懂2个根本问题:什么是请求?为什么需要请求?
1. 什么是“请求”?
- 生活类比:你(浏览器/前端)向超市(服务器)要牛奶(数据/资源),“要”的动作就是请求,超市给你牛奶的动作是响应。
- 专业定义:客户端(浏览器)向服务器发起的“获取/提交数据/资源”的主动行为,通过HTTP/HTTPS协议传输。
2. 为什么需要请求?
- 静态网页:无请求,内容写死在HTML里,无法更新(比如本地打开的纯文本页面);
- 动态网页:通过请求获取服务器最新数据(如新闻、评论)或提交数据(如注册、发布内容),让网页“活”起来。
二、核心概念:什么是Ajax?(异步请求的核心方案)
1. Ajax定义
Ajax(Asynchronous JavaScript and XML)是一套异步数据交互方案,核心组合:JS逻辑 + XHR/Fetch(请求工具) + JSON/XML(数据格式)。
- 核心关键词:异步(请求不阻塞页面)、无刷新(局部更新,不用整页刷)、数据交互(只传数据,不传完整HTML)。
2. Ajax的价值
解决早期“同步请求”的痛点:
- 同步请求:点击按钮后页面卡住,需等待完整HTML返回才能操作;
- Ajax异步请求:请求时页面正常操作,只获取需要的数据,效率更高。
3. Ajax与XHR的关系
- XHR是“工具”:浏览器内置的底层API,专门实现异步请求,是Ajax的核心依赖;
- Ajax是“方案”:用XHR(或Fetch)实现异步数据交互的技术组合;
- 结论:用XHR写异步请求,就是在写Ajax代码。
三、XHR基础:Ajax的核心工具(异步请求信使)
XHR(XMLHttpRequest)是实现Ajax的核心工具,以下是必须掌握的基础:
1. XHR核心属性(控制请求/获取响应)
| 属性名 | 作用描述 |
|---|---|
readyState | 请求状态码(0-4),标志请求生命周期(0=未初始化,4=请求完成) |
status | HTTP响应状态码(200=成功,404=资源未找到,500=服务器错误) |
responseText | 服务器返回的字符串格式数据(常用) |
timeout | 请求超时时间(毫秒,如5000=5秒超时) |
2. XHR核心事件(监听请求状态)
| 事件名 | 触发时机 | 用途 |
|---|---|---|
onload | readyState === 4(请求完成) | 处理响应数据(常用) |
onerror | 网络错误(断网、地址无效) | 处理网络异常 |
ontimeout | 超过timeout时间未响应 | 处理超时异常 |
3. XHR使用固定步骤(Ajax请求模板)
- 创建XHR实例 → 2. 配置请求(method、url、异步) → 3. (可选)设置请求头/超时 → 4. 监听响应 → 5. 发送请求
四、XHR实战:简化版Ajax请求(无页面渲染)
所有实战代码仅用控制台打印结果,聚焦请求核心逻辑,无需关注页面渲染。
4.1 环境准备
- 创建文件夹
ajax-xhr-simple,内部结构:
ajax-xhr-simple/
├─ data.json (本地数据源)
└─ xhr-demo.html (请求代码)
data.json内容:
{ "status": "success", "data": ["前端教程", "Ajax实战"] }
- 启动服务器:VS Code安装「Live Server」,右键
xhr-demo.html→「Open with Live Server」(本地请求必须用服务器,否则跨域)。
4.2 实战1:Ajax+XHR异步请求本地JSON(GET)
<!DOCTYPE html>
<html lang="zh-CN">
<body>
<script>
// 1. 创建XHR实例(Ajax的请求工具)
const xhr = new XMLHttpRequest();
// 2. 配置Ajax请求:GET方式、请求地址、async=true(异步,Ajax核心)
xhr.open('GET', 'data.json', true);
// 3. 设置超时时间(5秒)
xhr.timeout = 5000;
// 4. 监听响应(请求完成后触发,处理数据)
xhr.onload = function() {
// 判断请求是否成功:HTTP状态码200-299(服务器成功响应)
if (xhr.status >= 200 && xhr.status < 300) {
// 解析JSON字符串为JS对象(Ajax常用数据格式)
const response = JSON.parse(xhr.responseText);
console.log('Ajax GET请求本地JSON成功:', response);
} else {
console.error('Ajax GET请求失败,状态码:', xhr.status);
}
};
// 5. 监听网络错误
xhr.onerror = function() {
console.error('Ajax GET请求网络错误');
};
// 6. 监听超时
xhr.ontimeout = function() {
console.error('Ajax GET请求超时(5秒)');
};
// 7. 发送请求(GET请求无请求体,传null)
xhr.send(null);
</script>
</body>
</html>
- 运行结果:控制台打印
{status: "success", data: ["前端教程", "Ajax实战"]}。
4.3 实战2:Ajax+XHR请求真实API(GET)
使用免费测试API(JSONPlaceholder,支持跨域):
<!DOCTYPE html>
<html lang="zh-CN">
<body>
<script>
// 1. 创建XHR实例
const xhr = new XMLHttpRequest();
// 2. 配置Ajax请求:真实API地址(获取文章列表,带参数limit=2)
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts?limit=2', true);
// 3. 监听响应
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
const posts = JSON.parse(xhr.responseText);
console.log('Ajax GET请求真实API成功:', posts);
} else {
console.error('API请求失败,状态码:', xhr.status);
}
};
// 4. 发送请求
xhr.send(null);
</script>
</body>
</html>
- 运行结果:控制台打印2条来自远程服务器的文章数据。
4.4 实战3:Ajax+XHR提交数据(POST)
POST用于提交数据,核心是设置请求头和请求体:
<!DOCTYPE html>
<html lang="zh-CN">
<body>
<script>
// 1. 创建XHR实例
const xhr = new XMLHttpRequest();
// 2. 配置Ajax POST请求:方法为POST,地址为提交接口
xhr.open('POST', 'https://jsonplaceholder.typicode.com/comments', true);
// 3. 关键:设置请求头,告诉服务器请求体是JSON格式(POST必选)
xhr.setRequestHeader('Content-Type', 'application/json');
// 4. 监听响应
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
const response = JSON.parse(xhr.responseText);
console.log('Ajax POST提交成功,服务器返回:', response);
} else {
console.error('POST提交失败,状态码:', xhr.status);
}
};
// 5. 准备提交的数据(JSON对象)
const postData = {
name: 'Ajax测试',
email: 'test@example.com',
body: '这是POST提交的测试数据'
};
// 6. 发送请求:将JSON对象转为字符串(POST请求必须传请求体)
xhr.send(JSON.stringify(postData));
</script>
</body>
</html>
- 运行结果:控制台打印服务器返回的提交结果(含自动生成的ID)。
五、关键知识:什么是跨域?为什么会跨域?怎么解决?
在本地请求或调用不同域名API时,经常会遇到“跨域错误”——这是前端开发的高频问题,必须掌握。
1. 什么是跨域?
- 定义:浏览器的“同源策略”限制,当请求的协议、域名、端口三者任意一个与当前页面不同时,就属于跨域。
- 同源示例:当前页面
http://localhost:5500,请求http://localhost:5500/data.json(协议、域名、端口都相同,同源,允许请求); - 跨域示例:
-
- 本地双击HTML(协议
file://)请求data.json(跨域); - 页面
http://localhost:5500请求https://api.xxx.com(协议不同,跨域); - 页面
http://a.com请求http://b.com(域名不同,跨域); - 页面
http://localhost:5500请求http://localhost:8080(端口不同,跨域)。
- 本地双击HTML(协议
2. 为什么会有跨域限制?(同源策略的目的)
为了安全!防止恶意网站窃取你的数据:
- 比如你登录了银行网站(
https://bank.com),又打开了恶意网站(https://hack.com); - 若没有同源策略,恶意网站可通过请求获取你银行的登录状态和数据,造成安全风险。
3. 常见跨域解决方案(0基础友好)
(1)本地开发:用服务器环境(解决本地跨域)
- 问题:直接双击HTML文件(
file://协议)请求本地JSON,跨域报错; - 解决:用VS Code「Live Server」或Python临时服务器打开文件,让页面和请求地址同源(如
http://localhost:5500)。
(2)真实项目:CORS(跨域资源共享,最常用)
- 原理:服务器在响应头中添加
Access-Control-Allow-Origin: *(允许所有域名请求),告诉浏览器“这个请求是安全的,可以放行”; - 示例:我们用的JSONPlaceholder API,服务器就配置了CORS,所以
http://localhost:5500能正常请求它; - 特点:前端无需改代码,由后端配置,简单高效。
(3)其他方案:JSONP(兼容旧浏览器)
- 原理:利用
<script>标签不受同源策略限制的特性,通过动态创建<script>标签请求数据; - 局限:仅支持GET请求,不支持POST,现在已逐渐被CORS替代。
(4)开发环境:代理服务器(前端本地代理)
- 原理:在本地开发环境(如Vue、React脚手架)配置代理,让请求先转发到本地代理服务器,再由代理服务器请求目标API(代理服务器无同源限制);
- 示例:Vue脚手架配置
vue.config.js的devServer.proxy,React配置package.json的proxy。
3. 跨域错误示例(控制台报错)
Access to XMLHttpRequest at 'https://api.xxx.com' from origin 'http://localhost:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
- 意思:目标服务器未配置CORS,浏览器阻止了跨域请求。
六、Ajax进阶:封装通用XHR请求函数(简化重复代码)
多次写XHR代码会发现重复逻辑太多,封装成通用函数,后续使用只需一行代码:
6.1 封装代码(支持GET/POST,带跨域相关注释)
/**
* 通用Ajax请求封装(基于XHR)
* @param {string} method - 请求方式:GET/POST
* @param {string} url - 请求地址(本地JSON或API,注意跨域问题)
* @param {Object} [options={}] - 可选配置
* @param {Object} [options.data] - POST请求体数据
* @param {number} [options.timeout=5000] - 超时时间
* @returns {Promise<Object>} - 成功返回解析后的数据,失败返回错误
*/
function ajax(method, url, options = {}) {
const { data = null, timeout = 5000 } = options;
return new Promise((resolve, reject) => {
// 1. 创建XHR实例
const xhr = new XMLHttpRequest();
// 2. 配置请求(强制异步,Ajax核心)
xhr.open(method.toUpperCase(), url, true);
// 3. 设置超时
xhr.timeout = timeout;
// 4. POST请求设置请求头(告诉服务器数据格式)
if (data && method.toUpperCase() === 'POST') {
xhr.setRequestHeader('Content-Type', 'application/json');
}
// 5. 监听响应成功
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
try {
resolve(JSON.parse(xhr.responseText));
} catch (e) {
resolve(xhr.responseText); // 非JSON格式返回原始数据
}
} else {
reject(new Error(`请求失败:状态码${xhr.status}`));
}
};
// 6. 错误处理(含跨域错误,跨域时会触发onerror)
xhr.onerror = () => reject(new Error('请求失败:网络错误或跨域问题'));
xhr.ontimeout = () => reject(new Error(`请求超时(${timeout}ms)`));
// 7. 处理请求体数据
const requestData = data ? JSON.stringify(data
) : null;
// 8. 发送请求
xhr.send(requestData);
});
}
// 简化GET/POST方法(直接调用)
ajax.get = (url, options) => ajax('GET', url, options);
ajax.post = (url, data, options) => ajax('POST', url, { ...data, ...options });
6.2 封装后使用示例
<!DOCTYPE html>
<html lang="zh-CN">
<body>
<script>
// 粘贴上面的封装函数...
// 1. 用封装函数请求本地JSON(无跨域)
async function fetchLocal() {
try {
const data = await ajax.get('data.json');
console.log('封装函数请求本地JSON成功:', data);
} catch (error) {
console.error('请求失败:', error.message);
}
}
fetchLocal();
// 2. 用封装函数请求真实API(支持CORS,无跨域)
async function fetchApi() {
try {
const posts = await ajax.get('https://jsonplaceholder.typicode.com/posts?limit=2');
console.log('封装函数请求API成功:', posts);
} catch (error) {
console.error('API请求失败:', error.message);
}
}
fetchApi();
</script>
</body>
</html>
七、总结:核心知识点回顾
- 请求:前后端交互的桥梁,无请求则无动态网页;
- Ajax:异步请求方案,核心是“异步、无刷新、数据交互”;
- XHR:实现Ajax的底层工具,固定使用步骤(创建→配置→监听→发送);
- 跨域:同源策略导致,解决方案有本地服务器、CORS、代理等;
- 封装:减少重复代码,提高开发效率,支持
async/await简化回调。