AJAX 小白完全解析
一、先理解"同步"有多难受
没有 AJAX 的时候(传统网页)
你在淘宝看商品详情
↓
点"查看评价"按钮
↓
❌ 整个页面白屏 → 转圈加载 → 全新页面出现
↓
你刚才滚动到的位置没了
图片重新加载
页面闪了一下
每次交互都要刷新整个页面,用户体验极差。
二、AJAX 带来的改变
你在淘宝看商品详情
↓
点"查看评价"按钮
↓
✅ 页面不动,只有"评价区域"悄悄更新
↓
其他部分完全不受影响
你滚动到哪还在哪
局部更新,页面不刷新——这就是 AJAX 的核心价值。
三、生活类比:餐厅点餐
传统方式(没有 AJAX)
code复制
你坐在座位上
↓
起身走到厨房门口
↓
站着等厨师做完
↓
端着菜走回座位
↓
才能继续吃饭
问题:你人走了,座位空了,回来位置可能没了。
AJAX 方式
你坐在座位上
↓
按铃叫服务员(JS 发请求)
↓
你继续吃饭、聊天、刷手机(页面不卡)
↓
服务员把菜端过来(数据回来了)
↓
你继续吃,完全没中断
关键:你不用站起来等,服务员(AJAX)在后台帮你搞定。
四、技术层面:AJAX 到底是什么
AJAX = Asynchronous JavaScript and XML
拆解:
A → Asynchronous(异步)→ 不等结果,先干别的
J → JavaScript → 用 JS 代码发请求
A → And →
X → XML → 数据格式(现在基本用 JSON)
虽然名字里有 XML,但现在 99% 的场景用 JSON,所以你可以理解为 AJAJ(Asynchronous JavaScript and JSON)。
五、代码层面:怎么实现的
最原始的方式:XMLHttpRequest
js
// 1. 创建一个"服务员"对象
const xhr = new XMLHttpRequest();
// 2. 配置请求:用什么方法、去哪拿数据
xhr.open('GET', 'https://api.example.com/user/123', true);
// 方法 URL地址 是否异步
// 3. 告诉服务员:拿到数据后告诉我
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
// 数据回来了!
const data = JSON.parse(xhr.responseText);
console.log(data.name); // "小明"
}
};
// 4. 发送请求
xhr.send();
现代方式:fetch(更简洁)
js
fetch('https://api.example.com/user/123')
.then(response => response.json()) // 把响应转成 JSON
.then(data => {
console.log(data.name); // "小明"
// 更新页面某个部分
document.getElementById('username').textContent = data.name;
});
更现代的方式:async/await(最像人话)
js
async function getUser() {
const response = await fetch('https://api.example.com/user/123');
const data = await response.json();
console.log(data.name); // "小明"
document.getElementById('username').textContent = data.name;
}
getUser();
六、AJAX 的工作流程(图解)
code
用户点击按钮
│
▼
┌─────────────────┐
│ JavaScript │ ← 拦截点击事件,不刷新页面
│ 创建请求对象 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ 发送 HTTP 请求 │ ← 悄悄发给服务器(用户无感知)
│ (异步!) │
└────────┬────────┘
│
▼
用户继续操作页面
(滚动、点击、输入...)
页面完全不受影响 ✅
│
▼
┌─────────────────┐
│ 服务器处理请求 │ ← 查数据库、算数据...
│ 返回数据 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ JavaScript 收到 │
│ 数据,更新 DOM │ ← 只改需要变的部分
│ (局部更新) │
└─────────────────┘
七、为什么叫"异步"?
同步(Synchronous):排队等
js
// 同步代码:一行一行执行,上一行没完,下一行不跑
const result = fetchData(); // ← 卡在这里,等服务器返回
console.log(result); // ← 这行等上面完了才执行
// 页面卡住了,用户点不了按钮
异步(Asynchronous):先干别的
js
// 异步代码:发完请求继续往下走,不等待
fetch('https://api.example.com/data')
.then(data => {
// 数据回来了再执行这里
console.log(data);
});
console.log('这行先执行'); // ← 这行不等上面,立刻执行
// 页面不卡,用户随便操作
八、实际应用场景
| 场景 | 没有 AJAX | 有 AJAX |
|---|---|---|
| 微博加载更多 | 点击 → 整页刷新 | 点击 → 底部追加新内容 |
| 购物车加减 | 点击 → 整页刷新 | 点击 → 数字变一下 |
| 搜索建议 | 输入完回车 → 新页面 | 输入中实时显示建议 |
| 点赞 | 点击 → 整页刷新 | 点击 → 心变红,数字+1 |
| 表单验证 | 提交后才知道错 | 输完立刻提示错误 |
九、一句话总结
AJAX
Asynchronous JavaScript and XML 异步 JS 和 XML 技术, 是前后端 沟通的一种重要机制。
AJAX 就是:用 JavaScript 在后台悄悄跟服务器说话,拿到数据后只更新页面的一小部分,不用刷新整个页面。
核心就三个词:异步、无刷新、局部更新。🤐
XMLHttpRequest vs fetch + 实习/面试实用性
一、两者特征对比
| 特征 | XMLHttpRequest (XHR) | fetch |
|---|---|---|
| API 风格 | 事件驱动(onload, onerror, onreadystatechange) | 基于 Promise(.then().catch()) |
| 代码量 | 较多(5-6 行起步) | 简洁(1-2 行) |
| 进度监控 | ✅ 原生支持(onprogress) | ⚠️ 需要 ReadableStream |
| 同步请求 | ✅ 支持(不推荐) | ❌ 不支持 |
| 取消请求 | ✅ 支持(xhr.abort()) | ⚠️ 需要 AbortController |
| IE 兼容 | ✅ IE7+ | ❌ IE 不支持 |
| 现代项目使用率 | 极少( legacy 代码) | 绝对主流 |
二、实习中怎么写?【仅个人所了解到的一些见解】
现实情况
code
现代前端项目:
├── 90% 用 fetch 或 axios(axios 底层也是 XHR,但封装好了)
├── 5% 用 XMLHttpRequest(老项目维护、特殊需求)
└── 5% 其他(WebSocket、GraphQL 客户端等)
实习中你可能会看到的
js
// ✅ 现代写法(你实习大概率写这个)
const res = await fetch('/api/users');
const data = await res.json();
// 或者更常见的——用 axios(公司项目基本都用这个)
import axios from 'axios';
const { data } = await axios.get('/api/users');
js
// ❌ 实习中几乎不会手写这个(除非维护 5 年前的老代码)
const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/users', true);
xhr.onreadystatechange = function() { ... };
xhr.send();
三、AI 能生成,还要学吗?
直接回答:要学,而且必须学透
原因 1:AI 生成的东西会错,你得能看出来
js
// AI 可能生成这样(有 bug)
fetch('/api/login', {
method: 'POST',
body: { username: 'admin', password: '123' } // ❌ body 必须是字符串!
})
// 你得知道正确的是:
body: JSON.stringify({ username: 'admin', password: '123' })
原因 2:报错了你得会修
code
TypeError: Failed to fetch
CORS error
404 Not Found
500 Internal Server Error
AI 不会帮你调网络、看浏览器 Network 面板、抓包分析。这些调试能力只能靠你自己。
原因 3:面试不问"怎么写",问"为什么"
code
面试官不会问:"请手写一个 fetch 请求"
面试官会问:
- "fetch 和 axios 有什么区别?"
- "为什么 axios 可以拦截请求,fetch 不行?"
- "怎么取消一个 fetch 请求?"
- "fetch 的默认行为有什么坑?"
这些问题的答案,建立在你理解底层机制的基础上。
四、面试官到底问不问?
会问,但问法有层次
| 级别 | 问题示例 |
|---|---|
| 基础 | “fetch 返回的是什么?怎么拿到 JSON 数据?” |
| 进阶 | “fetch 和 XMLHttpRequest 的区别?各有什么场景?” |
| 深入 | “fetch 的第二个参数可以配置什么?怎么设置超时?” |
| 原理 | “axios 底层用什么实现的?为什么能支持请求/响应拦截器?” |
真实面试题(了解到的一些)
code
1. fetch 和 axios 的区别?
→ axios 基于 XHR,支持拦截器、自动 JSON 转换、请求取消
→ fetch 原生 API,更轻量,但需要手动处理很多细节
2. 用 fetch 发 POST 请求要注意什么?
→ headers 里加 'Content-Type': 'application/json'
→ body 必须是 JSON.stringify() 后的字符串
→ 默认不带 cookie,需要加 credentials: 'include'
3. 怎么让 fetch 支持超时?
→ fetch 本身没有超时参数
→ 需要用 Promise.race([fetch(), timeoutPromise])
五、学习的必要性总结
code
学 XMLHttpRequest 的目的:
└── 理解底层 HTTP 请求是怎么发出去的
└── 知道 fetch/axios 封装了什么
└── 遇到老项目能看懂
└── 面试能说出"为什么"
学 fetch 的目的:
└── 现代标准 API,所有项目都用
└── 理解 Promise、async/await 的最佳实践
└── 能独立写请求、处理错误、调试问题
不学 = 只会复制粘贴,报错就懵,面试被问就挂
六、找到的给实习生的一些建议
现在该学什么
code
优先级 1(必须熟):fetch + async/await + 错误处理
优先级 2(必须会):axios 的使用 + 拦截器配置
优先级 3(了解即可):XMLHttpRequest 的原理和基本用法
优先级 4(加分项):请求取消、重试机制、缓存策略
实习中实际 workflow
code
遇到要发请求的场景:
1. 先看项目里用的什么(axios?fetch?封装的工具函数?)
2. 复制一个已有的例子,改 URL 和参数
3. 用 AI 辅助生成,但自己检查一遍
4. 打开浏览器 Network 面板,确认请求发出去了、参数对了
5. 处理错误情况(loading、失败提示)
七、一句话
AI 能帮你写代码,但不能帮你理解代码、调试代码、面试回答为什么。
学 AJAX 不是学"怎么敲",是学"HTTP 请求是怎么回事",这个底层逻辑永远有用。
面试官问的不是你记不记得 API,是你遇到问题能不能独立解决。🤐
fetch vs axios 深度对比
一、它们是什么
| fetch | axios | |
|---|---|---|
| 本质 | 浏览器原生 API | 第三方库(npm install axios) |
| 底层 | 基于 Promise 的浏览器 API | 基于 XMLHttpRequest |
| 体积 | 0(浏览器自带) | ~13KB(gzip 后) |
| 兼容性 | 现代浏览器(IE 不支持) | 全浏览器兼容 |
💡 关键认知:axios 不是 fetch 的替代品,axios 底层用的是
XMLHttpRequest,只是封装得更友好。
二、核心差异对比
1. API 风格
js
// ✅ fetch — 原生 Promise,更底层
const res = await fetch('/api/user');
const data = await res.json(); // 需要手动解析
js
// ✅ axios — 自动处理,更省事
const { data } = await axios.get('/api/user'); // 自动转 JSON
2. 错误处理(大坑!)
js
// ❌ fetch 的坑:HTTP 错误码(404/500)不会抛错!
const res = await fetch('/api/not-found');
console.log(res.ok); // false ← 但代码没报错,继续执行
console.log(res.status); // 404
// ✅ 需要手动判断
if (!res.ok) {
throw new Error(`HTTP ${res.status}`);
}
js
// ✅ axios:HTTP 错误自动抛错,直接进 catch
try {
await axios.get('/api/not-found'); // 404 → 直接抛错
} catch (err) {
console.log(err.response.status); // 404
}
fetch 的默认行为:只有网络错误(断网、CORS 失败)才会 reject,HTTP 4xx/5xx 都算"成功响应"。
3. 请求体处理
js
// ❌ fetch:手动 stringify,手动设 header
await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: 'admin', password: '123' })
});
js
// ✅ axios:自动处理
await axios.post('/api/login', {
username: 'admin',
password: '123'
// 自动 JSON.stringify + 自动设 Content-Type
});
4. 拦截器(axios 的核心优势)
js
// axios 请求拦截器:每个请求自动加 token
axios.interceptors.request.use(config => {
config.headers.Authorization = `Bearer ${localStorage.getItem('token')}`;
return config;
});
// axios 响应拦截器:统一处理错误
axios.interceptors.response.use(
res => res, // 成功直接过
err => {
if (err.response.status === 401) {
window.location.href = '/login'; // 未登录,跳登录页
}
return Promise.reject(err);
}
);
fetch 没有原生拦截器,要自己封装:
js
// fetch 实现拦截器 = 自己包一层函数
async function myFetch(url, options = {}) {
// 请求前:加 token
options.headers = {
...options.headers,
Authorization: `Bearer ${localStorage.getItem('token')}`
};
const res = await fetch(url, options);
// 响应后:统一处理
if (res.status === 401) {
window.location.href = '/login';
}
return res;
}
5. 请求取消
js
// ✅ axios:原生支持取消
const source = axios.CancelToken.source();
axios.get('/api/slow', {
cancelToken: source.token
});
// 5秒后取消
setTimeout(() => source.cancel('用户取消了'), 5000);
js
// ⚠️ fetch:需要 AbortController(较新 API)
const controller = new AbortController();
fetch('/api/slow', {
signal: controller.signal
});
// 取消
controller.abort();
6. 超时处理
js
// ❌ fetch:没有原生超时参数
// 需要自己封装
const fetchWithTimeout = (url, options = {}, timeout = 5000) => {
return Promise.race([
fetch(url, options),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('超时')), timeout)
)
]);
};
js
// ✅ axios:一行配置
axios.get('/api/slow', { timeout: 5000 });
7. 进度监控(上传/下载)
js
// ✅ axios:原生支持上传/下载进度
axios.get('/api/file', {
onDownloadProgress: (progressEvent) => {
const percent = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
);
console.log(`下载进度:${percent}%`);
}
});
js
// ⚠️ fetch:需要 ReadableStream,代码复杂很多
const res = await fetch('/api/file');
const reader = res.body.getReader();
const total = +res.headers.get('Content-Length');
let loaded = 0;
while (true) {
const { done, value } = await reader.read();
if (done) break;
loaded += value.length;
console.log(`进度:${Math.round(loaded * 100 / total)}%`);
}
三、为什么 axios 能拦截,fetch 不能?
code
axios 底层 = XMLHttpRequest(对象,可扩展)
创建 xhr 对象 → 可以往对象上挂拦截器函数
发请求前 → 先执行请求拦截器
收到响应 → 先执行响应拦截器
然后才返回给调用方
fetch 底层 = 浏览器原生 API(黑盒)
调用 fetch() → 浏览器内部处理 → 直接返回 Promise
你无法在"发请求前"插入逻辑
也无法在"收到响应后"统一处理
除非自己包一层函数
四、fetch 的默认行为坑
| 坑 | 说明 |
|---|---|
| 不自动带 cookie | 需要手动加 credentials: 'include' |
| 4xx/5xx 不抛错 | 需要手动判断 res.ok |
| 不自动转 JSON | 需要手动 res.json() |
| 没有超时 | 需要自己封装 |
| 没有进度 | 需要用 ReadableStream |
js
// fetch 的正确打开方式(防坑版)
async function safeFetch(url, options = {}) {
const res = await fetch(url, {
credentials: 'include', // 带 cookie
headers: {
'Content-Type': 'application/json',
...options.headers
},
...options
});
if (!res.ok) {
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
}
return res.json(); // 自动解析 JSON
}
五、什么时候用哪个?
| 场景 | 推荐 | 原因 |
|---|---|---|
| 简单请求、Demo、学习 | fetch | 零依赖,原生 API |
| 企业项目、生产环境 | axios | 功能完整,生态成熟 |
| 需要进度条 | axios | 原生支持 |
| 需要拦截器统一处理 | axios | 原生支持 |
| 对包体积敏感(小程序) | fetch | 不用额外引入 |
| 老项目维护 | 看项目用啥 | 保持一致 |
六、面试回答模板【谨供参考 具体情况具体看】
code
问:fetch 和 axios 有什么区别?
答:
1. 本质不同:fetch 是浏览器原生 API,axios 是基于 XMLHttpRequest 的第三方库
2. 功能差异:axios 有原生拦截器、自动 JSON 转换、请求取消、超时配置;fetch 这些都要自己封装
3. 错误处理:axios 对 4xx/5xx 自动抛错,fetch 只会在网络错误时抛错
4. 使用场景:简单场景用 fetch,企业项目基本用 axios
5. 底层原理:axios 基于 XHR 对象,可以在请求前后插入逻辑;fetch 是黑盒 API,无法直接拦截
总结:fetch 是"毛坯房",axios 是"精装修"。学 fetch 理解原理,用 axios 做项目。🤐