jQuery AJAX异步请求实战教程从getpost到jsonp跨域常见报错原因及高效处理方案完全指南
先聊聊AJAX这东西到底在干嘛
说实话,我第一次接触AJAX的时候完全懵圈。AJAX的全称是Asynchronous JavaScript and XML,翻译成大白话就是:不用刷新整个页面,就能跟服务器偷偷传输数据。
想象一下,你在写一封邮件,写了一半不小心按了刷新键,好家伙,全没了。传统的网页就是这种死板行为——每次你提交数据,整个页面都得重新加载。而AJAX呢?它就像手机后台下载文件一样,悄无声息地在后台跟服务器对话,你完全感觉不到页面在”折腾”。
jQuery把AJAX给封装得极其优雅,几行代码就能搞定复杂的异步请求。下面咱们不废话,直接上干货。
GET请求:最基础的请求方式
GET请求就像是去餐厅点菜,你在菜单上指一指,服务员就去厨房帮你做了。参数会直接暴露在URL后面,适合获取数据,不适合传敏感信息。
$.ajax({
url: 'https://api.example.com/users',
type: 'GET',
data: {
page: 1,
pageSize: 10,
keyword: 'jquery'
},
dataType: 'json',
success: function(data) {
console.log('获取成功', data);
$('#userList').html(data.list.map(user => `<li>${user.name}</li>`).join(''));
},
error: function(xhr, status, error) {
console.error('请求失败:', status, error);
}
});
上面这段代码做的事情很直接:向服务器要数据,拿到数据之后把用户列表渲染到页面上。dataType: 'json' 是告诉jQuery服务器返回的是JSON格式的数据,这样你拿到的data就已经是解析好的对象了,不用再手动JSON.parse。
有个小细节很多人不知道:GET请求的data参数会被自动序列化成query string,所以上面代码发出去的请求实际上是:
GET https://api.example.com/users?page=1&pageSize=10&keyword=jquery
POST请求:提交数据的首选
POST请求就像是你把饭菜单塞进厨房,参数在请求体里,URL看起来很干净,适合提交敏感数据或者大量数据。
$.ajax({
url: 'https://api.example.com/users',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
name: '张三',
email: 'zhangsan@example.com',
age: 28
}),
dataType: 'json',
headers: {
'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
},
success: function(data) {
console.log('创建成功', data);
alert('用户创建成功,ID: ' + data.id);
},
error: function(xhr) {
if (xhr.status === 400) {
alert('参数错误,请检查输入');
} else if (xhr.status === 401) {
alert('登录已过期,请重新登录');
} else {
alert('服务器开小差了,请稍后再试');
}
}
});
注意contentType: 'application/json'这行,如果少了它,服务器收到的会是表单格式的数据而不是JSON,很多后端同学为此抓狂过。JSON.stringify把JS对象转成字符串,这是POST请求传JSON数据的标准姿势。
两种简洁写法:\(.get和\).post
如果你觉得上面的写法太长,jQuery提供了更简洁的写法:
// GET请求简洁版
$.get('https://api.example.com/users', { page: 1 }, function(data) {
console.log(data);
});
// POST请求简洁版
$.post('https://api.example.com/users', { name: '张三', email: 'zhangsan@example.com' }, function(data) {
console.log('创建成功', data);
});
简洁归简洁,但灵活性也少了。比如在POST请求里加自定义header、设置超时时间这些操作,简洁写法就搞不定了,还是得用完整的$.ajax。
JSONP跨域:老项目的救星
说到跨域,很多新同学可能一脸懵。简单解释一下:浏览器的安全策略规定,A网站的JavaScript不能去请求B网站的接口,这叫同源策略。比如你在www.a.com的页面上去请求www.b.com/api/data,浏览器会直接拦截。
JSONP就是当年前端工程师们想出来的一个”野路子”解决方案,它的核心原理是:<script>标签不受同源策略限制。
$.ajax({
url: 'https://api.example.com/data',
type: 'GET',
dataType: 'jsonp',
jsonp: 'callback',
jsonpCallback: 'handleData',
success: function(data) {
console.log('jsonp数据', data);
},
error: function(xhr, status, error) {
console.error('jsonp请求失败', error);
}
});
// 定义回调函数
function handleData(data) {
console.log('收到数据', data);
}
jQuery会自动帮你生成一个带时间戳的随机回调函数名(比如handleData1625098765432),请求URL会变成:
https://api.example.com/data?callback=handleData1625098765432
服务器收到请求后,会返回类似这样的代码:
handleData1625098765432({"name": "张三", "age": 28});
浏览器执行这段JS代码,你的回调函数就拿到了数据。
但要注意,JSONP只能发GET请求,而且依赖服务器配合返回回调函数格式的数据。现在很多新项目已经用CORS替代JSONP了,但老项目里还是能见到它的身影。
常见报错原因及处理方案
1. CORS跨域错误
这是目前最常见的错误,长这样:
Access to XMLHttpRequest at 'https://api.example.com/data' from origin 'https://www.a.com'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
原因:服务器没有配置允许你当前域名访问的响应头。
解决方案:
- 让后端同学在响应头里加上
Access-Control-Allow-Origin: https://www.a.com - 开发环境可以配置代理(比如Webpack的
devServer.proxy) - 如果是自己能控制的接口,可以配置Nginx反向代理
// 开发环境代理配置(webpack.config.js)
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'https://api.example.com',
changeOrigin: true
}
}
}
};
2. 404 Not Found
GET https://api.example.com/users/999 404 (Not Found)
原因:接口地址写错了,或者资源不存在。
排查步骤:
- 先复制完整URL到浏览器地址栏,看看能不能直接访问
- 检查URL拼写,特别是大小写和末尾斜杠
- 确认请求方法是否正确,有的接口GET能访问但POST不行
// 增加调试日志,看清楚到底请求了什么
$.ajax({
url: 'https://api.example.com/users/999',
type: 'GET',
headers: {
'Accept': 'application/json'
},
error: function(xhr) {
console.log('请求URL:', xhr.responseText);
console.log('状态码:', xhr.status);
}
});
3. 401 Unauthorized / 403 Forbidden
POST https://api.example.com/users 401 (Unauthorized)
原因:登录过期、Token无效或者权限不足。
解决方案:
- 检查请求头里的认证信息是否正确携带
- Token过期后自动刷新并重试请求
// 自动刷新Token并重试的方案
let isRefreshing = false;
let failedQueue = [];
function processQueue(error, token = null) {
failedQueue.forEach(prom => {
if (error) {
prom.reject(error);
} else {
prom.resolve(token);
}
});
failedQueue = [];
}
function request(url, options) {
return $.ajax(url, options)
.fail(function(xhr) {
if (xhr.status === 401) {
if (!isRefreshing) {
isRefreshing = true;
return refreshToken()
.then(newToken => {
processQueue(null, newToken);
options.headers['Authorization'] = 'Bearer ' + newToken;
return $.ajax(url, options);
})
.catch(err => {
processQueue(err, null);
window.location.href = '/login';
})
.finally(() => {
isRefreshing = false;
});
} else {
return new Promise((resolve, reject) => {
failedQueue.push({ resolve, reject });
}).then(token => {
options.headers['Authorization'] = 'Bearer ' + token;
return $.ajax(url, options);
});
}
}
throw xhr;
});
}
4. 500 Internal Server Error
POST https://api.example.com/users 500 (Internal Server Error)
原因:服务器端出错了,可能是数据库连接失败、代码bug或者参数解析错误。
解决方案:
- 查看服务器日志定位具体错误
- 检查请求参数是否符合接口要求
- 尝试用
application/x-www-form-urlencoded格式传参试试
$.ajax({
url: 'https://api.example.com/users',
type: 'POST',
data: {
name: '张三',
email: 'zhangsan@example.com'
},
// 试试表单格式
contentType: 'application/x-www-form-urlencoded',
error: function(xhr) {
try {
const err = JSON.parse(xhr.responseText);
console.error('服务器错误详情:', err.message);
} catch(e) {
console.error('服务器错误:', xhr.responseText);
}
}
});
5. Request Timeout(请求超时)
$.ajax({
url: 'https://api.example.com/slow-data',
type: 'GET',
timeout: 5000, // 5秒超时
error: function(xhr, status, error) {
if (status === 'timeout') {
console.error('请求超时,请稍后再试');
}
}
});
超时设置要根据实际情况来,大数据量的接口可以适当延长。同时给用户一个友好的提示,不要让用户傻等。
6. SyntaxError: Unexpected token <
这个错误经常出现在前端代码里,看着很吓人但其实原因很简单:
SyntaxError: Unexpected token < in JSON at position 0
原因:你期望服务器返回JSON,但实际返回的是HTML错误页面(比如404页面或者服务器维护页面),HTML开头是<html>,所以解析JSON时报错。
排查:
- 打开浏览器开发者工具→Network面板
- 找到失败的请求,查看Response内容
- 看看返回的是不是HTML而不是JSON
$.ajax({
url: 'https://api.example.com/data',
type: 'GET',
dataType: 'json',
error: function(xhr, status, error) {
// 手动检查返回的内容类型
const contentType = xhr.getResponseHeader('Content-Type');
if (!contentType || !contentType.includes('json')) {
console.error('服务器返回的不是JSON:', xhr.responseText.substring(0, 200));
}
}
});
7. Mixed Content(混合内容)错误
Mixed Content: The page at 'https://www.example.com' was loaded over HTTPS,
but requested an insecure resource 'http://api.example.com/data'.
原因:你的页面是HTTPS加密的,但请求的接口是HTTP明文协议,浏览器直接拦截。
解决方案:
- 把接口URL改成HTTPS
- 或者使用相对路径
//api.example.com/data,让协议跟随页面
// 推荐写法,协议跟随页面
$.ajax({
url: '//api.example.com/data',
type: 'GET'
});
全局拦截器:让所有请求拥有统一行为
实际项目中,每个请求都写success/error太麻烦了。jQuery提供了全局事件来处理:
// 请求开始时显示loading
$(document).ajaxStart(function() {
$('#loading').show();
});
// 请求结束时隐藏loading
$(document).ajaxComplete(function() {
$('#loading').hide();
});
// 统一处理401
$(document).ajaxError(function(event, xhr, settings, thrownError) {
if (xhr.status === 401) {
console.warn('登录已过期,即将跳转登录页');
setTimeout(() => {
window.location.href = '/login';
}, 1500);
}
});
// 统一处理网络错误
$(document).ajaxError(function(event, xhr, settings, thrownError) {
if (xhr.status === 0) {
console.warn('网络连接失败,请检查网络');
}
});
这样写的好处是:业务代码里不用关心loading和错误处理,逻辑清晰了很多。
取消请求:应对用户快速切换的场景
有时候用户操作很快,比如搜索框快速输入,每次按键都发请求,后面发的请求可能先返回,导致数据错乱。jQuery的AJAX返回的是jqXHR对象,可以调用.abort()取消:
let currentRequest = null;
$('#searchInput').on('input', function() {
const keyword = $(this).val();
// 取消上一次未完成的请求
if (currentRequest) {
currentRequest.abort();
}
if (!keyword.trim()) return;
currentRequest = $.ajax({
url: 'https://api.example.com/search',
type: 'GET',
data: { keyword },
success: function(data) {
currentRequest = null;
renderResults(data);
},
error: function(xhr, status) {
if (status !== 'abort') {
currentRequest = null;
console.error('搜索失败', status);
}
}
});
});
注意错误回调里判断status !== 'abort',因为取消请求也会触发error回调,我们需要忽略这种情况。
性能优化建议
1. 缓存请求结果
重复数据没必要每次都去服务器拉:
const cache = {};
function getCachedData(key, url, data) {
if (cache[key]) {
console.log('命中缓存', key);
return Promise.resolve(cache[key]);
}
return $.ajax({ url, data }).then(response => {
cache[key] = response;
// 5分钟后过期
setTimeout(() => delete cache[key], 5 * 60 * 1000);
return response;
});
}
2. 合并请求
如果多个接口返回的数据需要同时展示,可以用$.when合并:
$.when(
$.get('/api/users'),
$.get('/api/orders'),
$.get('/api/stats')
).done(function(usersRes, ordersRes, statsRes) {
// 注意:每个参数是一个数组 [data, statusText, jqXHR]
renderDashboard(usersRes[0], ordersRes[0], statsRes[0]);
});
3. 避免内存泄漏
在组件销毁时取消所有进行中的请求:
// 保存请求引用
let requests = [];
// 发起请求时保存
requests.push($.ajax({
url: '/api/data',
success: function(data) { /* ... */ }
}));
// 组件销毁时取消
function destroy() {
requests.forEach(req => req.abort());
requests = [];
}
最后说几句
AJAX这东西看着简单,真正用起来坑还挺多的。跨域、超时、认证、取消请求……每一个都是实际项目中会遇到的问题。但只要你掌握了基本原理,遇到问题时知道往哪个方向排查,大部分情况都能快速解决。
记住几个关键点:CORS是跨域首选方案、JSONP是老项目备用、401/403要处理认证流程、混合内容要用HTTPS或相对路径、全局拦截器能让代码更整洁。把这些记在心里,写AJAX请求的时候心里就有底了。
