嘿,小朋友!我是 Agnes。今天我们要聊一个听起来很酷、其实超级有意思的话题——AJAX。
别被这个名字吓跑啦!AJAX 其实就是“让网页动起来的小魔法”。如果你发现有些网页点一下按钮,不用刷新整个页面,就能加载出新的内容(比如刷微博、看抖音推荐),那就是 AJAX 在背后偷偷干活呢!
而且呀,今天我们要学的东西,连 6 岁的小朋友都能听懂原理。为什么?因为我们用讲故事的方式来学,而不是背枯燥的公式。
准备好了吗?我们要开始这场“网页探险”啦!
🌟 第一部分:AJAX 到底是个啥?(6 岁也能懂的故事)
想象一下,你在家里吃午饭,但是米饭还没煮熟。
传统的网页(没有 AJAX)
以前的网页特别笨。你想加个菜,怎么办?全部重做!
- 你把整张桌子掀了。
- 把碗、盘子、筷子全扔了。
- 跑去厨房重新买米、买菜、做饭。
- 把桌子重新摆好,饭也做好了。
这叫页面刷新。是不是很麻烦?很浪费时间?而且你吃到一半的汤都洒了!
有 AJAX 的网页
现在有魔法助手 AJAX 了!
- 你坐在桌子前,正在吃米饭。
- 你说:“我要加个红烧肉!”
- AJAX 说:“好的,我去帮你买红烧肉,你继续吃米饭。”
- AJAX 跑向厨房(服务器),买回来红烧肉,直接放在你面前的盘子里。
- 你全程不用动,碗也没碎,只是多了一道菜!
这就是 AJAX 的核心原理:
在不刷新整个网页的情况下,偷偷去后台取数据,然后把取来的数据更新到网页的某个角落。
什么是 GET 和 POST?
AJAX 要去厨房“点菜”时,有两种方式:
1. GET 请求(点菜看菜单)
就像你问妈妈:“妈,今天有红烧肉吗?”
- 你只是查询信息。
- 你不会改变厨房里的任何东西。
- 就像你用手机搜索“北京天气”,这只是获取数据。
特点:安全、只读、数据放在 URL 后面。
2. POST 请求(下单做菜)
就像你对服务员说:“我要一份红烧肉!”
- 你要提交数据。
- 厨房会真的做一道菜(数据被改变了)。
- 就像你在网上注册账号、发表博客、转账付款,这些都要用 POST。
特点:会改变服务器数据、更安全(密码不会出现在 URL 里)。
🛠️ 第二部分:原生 JavaScript 怎么写 AJAX?(老派但有用)
以前,程序员用 XMLHttpRequest 写 AJAX。这个名字很长,我们叫它 XHR。
虽然现在已经不常用了,但你要知道它的存在,因为很多老代码里还有它。
发起 GET 请求(原生写法)
// 1. 创建 AJAX 对象
var xhr = new XMLHttpRequest();
// 2. 告诉它我们要去哪里(URL),用什么方法(GET)
xhr.open('GET', 'https://api.example.com/user/1');
// 3. 告诉它:当我收到回复时,执行这个函数
xhr.onload = function() {
// 如果成功了(状态码 200),就把数据打印出来
if (xhr.status >= 200 && xhr.status < 300) {
console.log('成功啦!数据是:', xhr.responseText);
} else {
console.log('出错了,错误码:', xhr.status);
}
};
// 4. 如果网络断了,也要处理
xhr.onerror = function() {
console.log('网络错误,请检查网络哦!');
};
// 5. 真正发送请求
xhr.send();
问题:你看,代码层层嵌套,像俄罗斯套娃一样,还容易写成“回调地狱”( Callback Hell )。而且,如果我要同时发 3 个请求,还得写 3 遍?太麻烦了!
🚀 第三部分:现代主角登场!Fetch API(浏览器内置)
后来,浏览器进化了!推出了一个叫 Fetch 的新技能。它更简洁,更像日常说话。
发起 GET 请求(Fetch 写法)
fetch('https://api.example.com/user/1')
.then(response => {
// 检查是否成功(Fetch 不会自动认为 404 是错误,需要手动判断)
if (!response.ok) {
throw new Error('网络响应错误:' + response.status);
}
// 把响应变成 JSON 格式
return response.json();
})
.then(data => {
// 拿到真正的数据
console.log('用户数据:', data);
})
.catch(error => {
// 如果上面任何一步出错,都会跑到这里
console.log('哎呀,出错了:', error);
});
优点:
- 代码更短,看起来更干净。
- 返回的是 Promise(承诺),意思是:“我现在去取数据,拿到后告诉你,你不用傻等。”
发起 POST 请求(Fetch 写法)
fetch('https://api.example.com/user', {
method: 'POST', // 告诉服务器,这是提交数据
headers: {
'Content-Type': 'application/json' // 告诉服务器:“我要给你的是 JSON 格式的数据”
},
body: JSON.stringify({ // 把 JavaScript 对象变成字符串
name: '小明',
age: 6,
favFood: '红烧肉'
})
})
.then(response => response.json()) // 把服务器的回复也解析成 JSON
.then(data => {
console.log('创建成功!新用户的 ID 是:', data.id);
})
.catch(error => {
console.log('提交失败:', error);
});
注意:Fetch 有个小脾气——如果服务器返回 404 或 500,它不会报错!它还是觉得成功了,只是数据是空的。所以我们要手动检查 response.ok。
🧰 第四部分:Axios——程序员的最爱(封装神器)
虽然 Fetch 很好,但很多程序员还是喜欢用 Axios。为什么?
因为 Axios 更“智能”:
- 它会自动把 JSON 数据转成字符串(不用手动
JSON.stringify)。 - 它会自动把服务器返回的字符串解析成 JSON(不用手动
.json())。 - 它有拦截器(Interceptors),可以在发送前和收到后偷偷做点事情(比如加令牌、处理全局错误)。
安装 Axios
如果你用浏览器直接引入:
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
如果你用 Node.js 或 npm:
npm install axios
GET 请求(Axios 写法)
axios.get('https://api.example.com/user/1')
.then(response => {
// 数据在 response.data 里
console.log('用户信息:', response.data);
})
.catch(error => {
console.log('出错了:', error);
});
POST 请求(Axios 写法)
axios.post('https://api.example.com/user', {
name: '小明',
age: 6
})
.then(response => {
console.log('创建成功:', response.data);
})
.catch(error => {
console.log('失败啦:', error);
});
哇!是不是比 Fetch 简洁多了? Axios 帮我们处理了很多繁琐的细节。
🔧 第五部分:超级封装——让请求像搭积木一样简单
现在,你是专家了!我们来自己写一个超级封装的工具。以后你只需要写一行代码,就能发请求,还能自动处理错误、显示加载动画。
步骤 1:创建 axios.js 文件
import axios from 'axios';
// 创建 Axios 实例(相当于创建了一个专属的小助手)
const request = axios.create({
baseURL: 'https://api.example.com', // 所有请求都自动带上这个前缀
timeout: 5000, // 如果 5 秒还没反应,就放弃
headers: {
'Content-Type': 'application/json'
}
});
// --- 拦截器:发送请求之前,偷偷做的事 ---
request.interceptors.request.use(
config => {
// 比如,从 localStorage 取出用户的登录令牌
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`; // 把令牌加到请求头里
}
// 显示加载动画
console.log('正在请求数据,请稍候...');
return config;
},
error => {
return Promise.reject(error);
}
);
// --- 拦截器:收到响应之后,偷偷做的事 ---
request.interceptors.response.use(
response => {
// 如果服务器返回 200,但数据里 code 不是 1,说明业务出错了
const res = response.data;
if (res.code === 1) {
return res.data; // 返回真正的数据
} else {
// 业务错误,弹出提示
alert('出错了:' + res.message);
return Promise.reject(new Error(res.message));
}
},
error => {
// 网络错误或 HTTP 错误(如 404、500)
console.error('网络错误:', error.message);
// 如果是 401,说明登录过期了,跳回登录页
if (error.response && error.response.status === 401) {
alert('登录已过期,请重新登录!');
window.location.href = '/login';
}
return Promise.reject(error);
}
);
// 导出封装好的 request
export default request;
步骤 2:在项目里使用(超级简洁!)
import request from './axios'; // 导入我们刚才封装的工具
// GET 请求:取用户信息
async function getUser() {
try {
// 直接拿到数据,不用 .then,不用 catch,因为 async/await 更优雅!
const userData = await request.get('/user/1');
console.log('用户名字:', userData.name);
} catch (error) {
// 这里只会捕获到封装里 reject 的错误
console.log('获取失败');
}
}
// POST 请求:创建用户
async function createUser() {
try {
const result = await request.post('/user', {
name: '小红',
age: 7
});
console.log('创建成功,新 ID:', result.id);
} catch (error) {
console.log('创建失败');
}
}
看! 代码是不是像读句子一样简单?getUser() 就像是说:“去,把用户 1 的信息拿来。”
🌍 第六部分:跨域——网页的“小区门禁”
什么是跨域?
想象你住在小区 A(localhost:3000),你想去小区 B(api.example.com)借东西。
小区 A 的门卫说:“不行! 你没经过允许,不能随便进 B 小区。这是浏览器的安全规定,叫同源策略。”
同源的意思是:协议(http/https)、域名(xxx.com)、端口(80/3000)都完全一样,才叫同源。
只要有一个不一样,就是跨域。
为什么要有跨域限制?
防止坏人偷偷访问你的银行网站,把你的钱转走。如果没有这个限制,任何网站都能偷偷读取你的数据,那世界就乱套了!
怎么解决跨域?(三种方法)
方法 1:后端设置 CORS(最推荐!)
让后端(服务器)在回复时,告诉浏览器:“我允许这个网站访问我!”
后端代码(以 Node.js 为例):
app.use((req, res, next) => {
// 允许来自 http://localhost:3000 的请求
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
// 允许发送 Cookie
res.setHeader('Access-Control-Allow-Credentials', 'true');
next();
});
前端不需要做任何事,请求就能成功!
方法 2:使用代理(开发时用)
在你自己的电脑上,搭一个“中转站”。
前端请求:http://localhost:3000/api/user (同源,没问题!)
中转站转发:http://api.example.com/user (服务器之间没有跨域限制)
配置方法(Vite 项目 vite.config.js):
export default {
server: {
proxy: {
'/api': {
target: 'https://api.example.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
}
方法 3:JSONP(古老方法,现在很少用)
利用 <script> 标签可以跨域加载资源的特性。
// 旧时代写法,不推荐,但有历史意义
<script>
function callback(data) {
console.log(data);
}
</script>
<script src="https://api.example.com/data?callback=callback"></script>
缺点:只能用于 GET 请求,而且不安全。
🐛 第七部分:错误拦截——别让程序崩溃
在实际项目中,网络可能断掉,服务器可能宕机,数据可能为空。我们要预判错误,而不是等程序报错。
常见错误类型
- 网络错误:手机没信号,WiFi 断了。
- HTTP 错误:服务器返回 404(找不到)、403(没权限)、500(服务器内部错误)。
- 业务错误:服务器返回 200,但数据里说“密码错误”。
封装错误处理函数
// utils/api.js
import request from './axios';
// 统一的错误处理函数
function handleApiError(error) {
if (error.response) {
// 服务器返回了错误状态码
switch (error.response.status) {
case 400:
alert('请求参数错误,请检查输入!');
break;
case 401:
alert('登录已过期,请重新登录!');
window.location.href = '/login';
break;
case 403:
alert('你没有权限访问这个功能!');
break;
case 404:
alert('找不到你要的数据!');
break;
case 500:
alert('服务器开小差了,请稍后再试!');
break;
default:
alert('未知错误:' + error.response.status);
}
} else if (error.request) {
// 网络断了,请求发出去了但没收到回复
alert('网络已断开,请检查你的网络连接!');
} else {
// 其他错误
alert('发生错误:' + error.message);
}
}
// 导出所有 API 请求
export const api = {
getUser: (id) => request.get(`/user/${id}`).catch(handleApiError),
createUser: (data) => request.post('/user', data).catch(handleApiError),
deleteUser: (id) => request.delete(`/user/${id}`).catch(handleApiError)
};
使用:
import { api } from './utils/api';
async function deleteUser() {
await api.deleteUser(1); // 如果有错误,会自动弹窗提示
}
🎓 第八部分:给 6 岁小朋友的总结
好了,小朋友,今天我们学了好多!我们来画一张地图,把知识串起来。
🗺️ AJAX 知识地图
[我要数据]
|
v
[选择方式] ----> GET(只看不改,像看书)
|
----> POST(要改数据,像写作业提交)
|
v
[发送请求]
|
v
[服务器] <-----> [浏览器] (中间有门卫:跨域)
|
v
[收到数据]
|
v
[处理数据] ----> 成功:显示给用户
|
----> 失败:弹窗告诉用户“出错了”
📝 三个主角对比
| 主角 | 特点 | 适合谁 |
|---|---|---|
| XHR | 老派,代码多,像用算盘 | 维护老项目 |
| Fetch | 现代,简洁,但有点小脾气 | 简单项目,不想装库 |
| Axios | 强大,智能,有拦截器 | 推荐! 大多数项目 |
💡 记住这几点
- AJAX = 不刷新页面,偷偷取数据。
- GET = 查询,POST = 提交。
- 跨域 = 浏览器的小区门禁,让后端开绿灯(CORS)或自己搭桥(代理)。
- 错误处理 = 永远不要相信
