第五部分:异步通信与后端交互

目录
第五部分:异步通信与后端交互
5.1. Ajax 与网络请求
5.1.1. 核心方法 $.ajax():配置项全解
5.1.2. 生命周期钩子:beforeSend, success, error, complete
5.1.3. 全局 Ajax 事件:ajaxStart, ajaxStop 与全局加载遮罩
5.2. 快捷方法与工具
5.2.1. $.get(), $.post(), $.getJSON(), $.getScript()
1. GET 请求获取数据
2. POST 请求提交数据
3. getJSON:专门获取 JSON 数据,自动解析
4. getScript:动态加载并执行 JS 脚本
5.2.2. 序列化工具:$.param() 与表单序列化 .serialize()
方法一:直接序列化整个表单
方法二:序列化普通对象
5.3. Promise 与 Deferred 对象
5.3.1. Deferred 对象创建:$.Deferred() 与状态管理
5.3.2. Promise 风格调用:解决回调地狱
传统写法
Promise 链式写法
5.3.3. 并发控制:$.when(…).done() 实现多请求并行
定义三个并行的异步请求
并发执行,全部成功后才触发 done
5.3.4. jQuery Promise vs ES6 Promise 的差异与兼容性
jQuery Promise 的错误处理缺陷示例
ES6 Promise 的改进
5.4. 跨域与安全
5.4.1. CORS 跨域配置:crossDomain 参数与 withCredentials 凭证携带
5.4.2. 安全防护:XSRF/CSRF Token 处理策略与 XSS 防御
防御策略对比
CSRF Token 处理
XSS 防御示例
5.4.3. 历史遗留:JSONP 原理回顾(现代开发已弃用)
5.1. Ajax 与网络请求
5.1.1. 核心方法 $.ajax():配置项全解
$.ajax() 是 jQuery 异步通信体系的基石,提供了高度可配置的 HTTP 请求能力。
|
核心配置项 |
类型/默认值 |
功能说明 |
|
url |
String |
请求的目标地址,后端 API 端点。 |
|
method / type |
String (GET) |
HTTP 请求方法,如 GET、POST、PUT、DELETE。 |
|
data |
Object / String |
发送到服务器的数据,jQuery 会自动转换为查询字符串或请求体。 |
|
dataType |
String (json) |
预期服务器返回的数据类型,常用 json、html、text。 |
|
contentType |
String |
发送数据时的编码类型,默认 application/x-www-form-urlencoded。 |
|
headers |
Object |
自定义请求头,常用于身份验证。 |
|
timeout |
Number |
请求超时时间(毫秒),超时后触发 error 回调。 |
作为底层方法,它不仅支持基础的 GET/POST 请求,还能精细控制缓存、头信息、数据类型转换等高级行为,是构建复杂 Web 应用的核心接口。
/* 完整的 $.ajax() 配置示例 */
$.ajax({
url: '/api/user/profile',
method: 'POST',
dataType: 'json', /* 预期后端返回JSON,jQuery自动解析 */
data: { id: 1001, action: 'update' }, /* 发送的对象数据 */
headers: {
'Authorization': 'Bearer token_xxxx' /* 携带身份令牌 */
},
timeout: 5000, /* 5秒超时保护 */
success: function(response) {
console.log('数据获取成功:', response);
},
error: function(xhr, status, err) {
console.error('请求失败:', status, err);
}
});
这就好比寄送一个重要包裹。
你需要填写收件地址、选择快递方式,如果包裹易碎还需要贴上特殊标签。
$.ajax() 就是那个详尽的快递单,通过勾选不同的配置项,确保数据能够按照预定方式安全抵达目的地或获得回执。
5.1.2. 生命周期钩子:beforeSend, success, error, complete
一个完整的 Ajax 请求如同一次外交活动,包含准备、成功/失败、收尾四个阶段。
|
钩子函数 |
触发时机 |
典型应用场景 |
|
beforeSend |
请求发送前 |
禁用提交按钮、显示 Loading 动画、修改请求头。 |
|
success |
请求成功时 |
解析数据、渲染页面、提示操作成功。 |
|
error |
请求失败时 |
错误日志上报、提示用户重试、处理 404/500 状态。 |
|
complete |
请求完成时 (无论成败) |
隐藏 Loading 动画、恢复按钮状态、执行清理逻辑。 |
jQuery 提供了对应的生命周期钩子,允许开发者在特定时间点介入,实现诸如加载动画控制、权限拦截等逻辑。
$.ajax({
url: '/api/submit',
type: 'POST',
data: $('#form').serialize(),
beforeSend: function() {
/* 请求发出前:防止用户重复点击,展示加载状态 */
$('#submitBtn').prop('disabled', true).text('提交中…');
$('#loading').show();
},
success: function(res) {
/* 业务逻辑成功 */
showToast('提交成功!');
},
error: function(xhr) {
/* 处理网络错误或服务器报错 */
showToast('服务器开小差了:' + xhr.status);
},
complete: function() {
/* 无论成功失败,都要恢复界面状态 */
$('#submitBtn').prop('disabled', false).text('提交');
$('#loading').hide();
}
});
想象一场正式的宴会:
- beforeSend 是宴会前的筹备工作,检查场地和邀请函;
- success 是宴会顺利进行,宾主尽欢;
- error 是中途发生意外,如停电或食材问题;
- complete 则是宴会结束后的收尾工作,无论过程如何,场地都需要清理复原,以便迎接下一次活动。
5.1.3. 全局 Ajax 事件:ajaxStart, ajaxStop 与全局加载遮罩
在单页应用(SPA)或复杂的页面交互中,往往同时存在多个 Ajax 请求。
为了避免每个请求都要单独处理加载状态,jQuery 提供了全局事件监听机制,允许在 document 对象上统一监控所有 Ajax 请求的开始与结束。
/* 绑定全局事件监听器 */
$(document).ajaxStart(function() {
/* 只要页面上有任意 Ajax 请求开始,且无其他未完成请求,即触发 */
$('#global-spinner').fadeIn(200);
});
$(document).ajaxStop(function() {
/* 当页面上所有 Ajax 请求都完成时触发 */
$('#global-spinner').fadeOut(200);
});
/* 如果某个特定请求不想触发全局事件,可配置 global: false */
$.ajax({
url: '/api/ping',
global: false, /* 静默请求,不触发全局 Loading */
success: function() { /* … */ }
});
|
事件名称 |
触发条件 |
备注说明 |
|
ajaxStart |
第一个请求开始时 |
适合启动全局遮罩。 |
|
ajaxSend |
每个请求发送前 |
可用于记录日志。 |
|
ajaxSuccess |
每个请求成功时 |
可统一预处理响应数据。 |
|
ajaxError |
每个请求失败时 |
可统一拦截错误码(如 401 跳转登录)。 |
|
ajaxComplete |
每个请求完成时 |
无论成败均触发。 |
|
ajaxStop |
所有请求结束时 |
适合关闭全局遮罩。 |
这就像是机场的塔台指挥系统。
无论天上飞着多少架飞机,塔台都有全局的雷达监控屏。
只要有一架飞机起飞,雷达屏就进入工作状态;
直到最后一架飞机降落,屏幕才恢复平静。
这种“全局视野”让开发者无需关注每架飞机的具体调度,也能掌握整体繁忙状态。
5.2. 快捷方法与工具
5.2.1. $.get(), $.post(), $.getJSON(), $.getScript()
对于简单的 GET 或 POST 请求,$.ajax() 的完整配置显得过于繁琐。
jQuery 封装了一系列快捷方法,将常用配置内置,简化了代码书写,提高了开发效率。
1. GET 请求获取数据
$.get('/api/articles', { page: 1 }, function(response) {
console.log('获取文章列表:', response);
}, 'json');
2. POST 请求提交数据
$.post('/api/login', { user: 'admin', pwd: '123456' }, function(res) {
if (res.token) {
localStorage.setItem('token', res.token);
}
});
3. getJSON:专门获取 JSON 数据,自动解析
$.getJSON('/api/config.json', function(data) {
/* data 已自动转为 JS 对象,无需 JSON.parse */
console.log('配置项:', data.version);
});
4. getScript:动态加载并执行 JS 脚本
$.getScript('/js/plugin.js', function() {
/* 脚本加载完毕并执行后回调 */
initPlugin(); /* 此时 plugin.js 中的函数已可用 */
});
|
方法 |
等效 $.ajax() 配置 |
典型用途 |
|
$.get(url, data, success) |
{ method: 'GET' } |
查询数据、获取页面片段。 |
|
$.post(url, data, success) |
{ method: 'POST' } |
表单提交、登录注册。 |
|
$.getJSON(url, success) |
{ dataType: 'json' } |
加载配置文件、API 数据交互。 |
|
$.getScript(url, success) |
{ dataType: 'script' } |
按需加载第三方库、延迟加载。 |
这些快捷方法就像是便利店里的“预调饮料”。
相比于 $.ajax() 这个专业的咖啡机(需要自己磨豆、调温、萃取),快捷方法直接提供了一杯调好的美式或拿铁,开盖即饮,适合不需要个性化定制的大多数标准场景。
5.2.2. 序列化工具:$.param() 与表单序列化 .serialize()
在前后端交互中,前端经常需要将表单数据或 JS 对象转换为 URL 编码的字符串格式。
jQuery 提供了 .serialize() 和 $.param() 两个工具方法,极大简化了数据拼接的工作。
|
方法 |
作用对象 |
输出格式示例 |
|
.serialize() |
jQuery 表单元素集合 |
name=John&age=20 |
|
.serializeArray() |
jQuery 表单元素集合 |
[{name:'name', value:'John'}, …] |
|
$.param() |
普通 JS 对象或数组 |
user[name]=John&user[age]=20 |
场景:复杂的表单提交
<form id="register">
<input name="username" value="test">
<input name="password" value="123">
<input name="hobbies[]" value="coding"> <!– 数组类型 –>
<input name="hobbies[]" value="reading">
</form>
方法一:直接序列化整个表单
$('#register').serialize();
/* 输出: username=test&password=123&hobbies%5B%5D=coding&hobbies%5B%5D=reading */
方法二:序列化普通对象
var filterParams = { status: 'active', role: 'admin' };
$.param(filterParams);
/* 输出: status=active&role=admin */
/* 应用:直接用于 Ajax 请求 */
$.post('/api/update', $('#register').serialize(), function(res) {
/* … */
});
这就像是搬家公司打包行李。
- .serialize() 是标准的打包箱服务,扫描整个房间(表单),把所有散落的物品整齐地码进箱子,并贴上标签。
- $.param() 则是人工打包,你可以自定义把哪些零散物品(JS 对象属性)打包成特定的形状,方便运输给后端。
5.3. Promise 与 Deferred 对象
5.3.1. Deferred 对象创建:$.Deferred() 与状态管理
jQuery 的 $.Deferred() 是异步编程的核心模型,它引入了状态管理的概念。
|
状态 |
含义 |
触发方法 |
可绑定回调 |
|
pending |
初始状态,操作未完成 |
– |
progress() |
|
resolved |
操作成功 |
resolve() |
done() |
|
rejected |
操作失败 |
reject() |
fail() |
一个 Deferred 对象有三种状态:待定、已解决和已拒绝,且状态一旦改变就不可逆转,这是实现异步链式调用的基础。
/* 创建一个耗时任务的 Promise 封装 */
function asyncTask() {
var dtd = $.Deferred(); /* 创建延迟对象 */
setTimeout(function() {
var success = Math.random() > 0.5;
if (success) {
dtd.resolve('任务完成'); /* 标记为成功 */
} else {
dtd.reject('出错了'); /* 标记为失败 */
}
}, 1000);
return dtd.promise(); /* 返回只读的 Promise 对象,防止外部修改状态 */
}
/* 使用示例 */
asyncTask()
.done(function(msg) { console.log('成功:', msg); })
.fail(function(err) { console.log('失败:', err); });
这就好比购买了一张演唱会门票。
- 演唱会开始前,状态是“待定”;
- 演出如期举行,状态变为“已解决”,你可以进场观看;
- 若演出取消,状态变为“已拒绝”,你可以申请退款。
- 一旦演出结束或取消,这个历史事实就无法改变,不能说昨天“已解决”的演出今天突然变成“已拒绝”了。
5.3.2. Promise 风格调用:解决回调地狱
传统的 Ajax 回调写法在处理多层嵌套请求时会产生“回调地狱”。
jQuery 的 Deferred 对象支持链式调用,通过 .then() 方法将异步操作拉平,使代码逻辑线性化、可读性大幅提升。
传统写法
$.ajax({
url: '/api/user',
success: function(user) {
$.ajax({
url: '/api/orders',
data: { userId: user.id },
success: function(orders) {
$.ajax({
url: '/api/details',
data: { orderId: orders[0].id },
success: function(details) {
console.log('最终详情:', details); /* 嵌套三层,难以维护 */
}
});
}
});
}
});
Promise 链式写法
$.ajax({ url: '/api/user' })
.then(function(user) {
return $.ajax({ url: '/api/orders', data: { userId: user.id } });
})
.then(function(orders) {
return $.ajax({ url: '/api/details', data: { orderId: orders[0].id } });
})
.done(function(details) {
console.log('最终详情:', details); /* 逻辑清晰,层层递进 */
})
.fail(function(err) {
console.error('任一步骤出错,统一在此捕获:', err);
});
回调地狱就像是把车开进了盘山公路,每一个弯道(异步操作)都套在上一个弯道里面,绕得人晕头转向。
Promise 链式调用则是修了一条直线隧道,无论中间经过多少个站点,从外面看都是一条笔直的道路,从起点直达终点,中途任何故障也都有统一的出口处理。
5.3.3. 并发控制:$.when(…).done() 实现多请求并行
在需要同时等待多个异步任务全部完成的场景下(如同时加载配置、用户信息和权限列表),$.when() 提供了类似 ES6 Promise.all 的并发控制能力,显著减少等待时间。
定义三个并行的异步请求
var req1 = $.get('/api/config');
var req2 = $.get('/api/user');
var req3 = $.get('/api/permissions');
并发执行,全部成功后才触发 done
$.when(req1, req2, req3).done(function(data1, data2, data3) {
/* 注意:每个参数是一个数组 [data, textStatus, jqXHR] */
console.log('配置:', data1[0]);
console.log('用户:', data2[0]);
console.log('权限:', data3[0]);
initApp(); /* 所有前置数据就绪,启动应用 */
}).fail(function(err) {
console.error('其中一个请求失败,初始化中止');
});
这就像是请客吃饭准备了三道菜。
厨师($.when)不需要等第一道菜做完才开始做第二道,而是三个灶台同时开火。
只有当三道菜全部做好了,才会通知“上菜”。
如果任何一道菜做坏了,整个宴席可能就要推迟或取消。
5.3.4. jQuery Promise vs ES6 Promise 的差异与兼容性
虽然 jQuery 的 Deferred 早于 ES6 Promise 出现,但两者在 API 设计和错误处理上存在微妙差异。
现代开发中推荐优先使用 ES6 标准,但在维护旧项目时仍需理解其区别。
|
特性 |
jQuery Deferred/Promise |
ES6 Promise |
|
错误捕获 |
若回调中报错,会静默吞掉,需手动 try-catch |
自动捕获异常,触发后续 reject |
|
状态触发 |
不仅有 resolve/reject,还有 notify/progress |
无进度通知机制 |
|
回调注册 |
.done(), .fail(), .always() |
.then(), .catch(), .finally() |
|
then 返回值 |
返回新的 Deferred 对象 |
返回新的 Promise 对象 |
|
兼容性 |
兼容 IE 等老旧浏览器 |
需 Polyfill 支持 |
jQuery Promise 的错误处理缺陷示例
$.ajax({ url: '/api/data' })
.done(function(data) {
/* 这里的代码如果报错,外部的 .fail 无法捕获 */
JSON.parse('invalid json');
})
.fail(function() {
/* 这里无法捕获 done 中的错误 */
});
ES6 Promise 的改进
fetch('/api/data')
.then(res => {
JSON.parse('invalid json'); /* 抛出错误 */
})
.catch(err => {
/* 可以正常捕获到 then 中的异常 */
console.error(err);
});
jQuery Promise 就像是老式的模拟对讲机,功能丰富甚至能发信号,但在抗干扰(错误处理)方面表现一般。
ES6 Promise 则是现代数字通信协议,虽然接口更简洁,但具备更健壮的异常处理机制。
在现代开发中,建议将 jQuery Ajax 返回的 Promise 转换为 ES6 标准,以保证一致性。
5.4. 跨域与安全
5.4.1. CORS 跨域配置:crossDomain 参数与 withCredentials 凭证携带
浏览器的同源策略默认阻止跨域请求,CORS(跨域资源共享)是现代标准解决方案。
|
配置项 |
作用 |
使用场景 |
|
crossDomain |
强制跨域请求 (默认自动检测) |
极少手动设置,除非同域请求需强制跨域行为 |
|
xhrFields |
设置原生 XHR 对象属性 |
withCredentials: true 用于携带 Cookie |
|
headers |
添加自定义头信息 |
满足服务器 CORS 预检要求 |
jQuery 在处理跨域时,虽然大部分工作由浏览器和服务器完成,但在携带 Cookie 等凭证时需显式配置。
/* 跨域请求并携带 Cookie (如 SSO 单点登录场景) */
$.ajax({
url: 'https://api.example.com/user/info',
type: 'GET',
xhrFields: {
withCredentials: true /* 允许跨域请求携带身份凭证 */
},
success: function(res) {
console.log('用户信息:', res);
}
});
注意:
服务器端必须配置 Access-Control-Allow-Credentials: true
且 Access-Control-Allow-Origin 不能为 *,必须指定域名
跨域请求就像是去另一个国家的海关。
普通请求只允许你把信扔过去。
但如果你需要“带行李”过去,就必须提前申请签证并走特殊通道。
withCredentials 就是这个特殊通道的通行证,允许你带着 Cookie 这个“行李”通过海关。
5.4.2. 安全防护:XSRF/CSRF Token 处理策略与 XSS 防御
Web 安全是前后端交互不可忽视的一环。
CSRF(跨站请求伪造)利用用户已认证的身份发起恶意请求,XSS(跨站脚本攻击)则通过注入恶意脚本窃取信息。
防御策略对比
|
攻击类型 |
攻击原理 |
jQuery 防御手段 |
|
CSRF |
伪造用户请求 |
请求头携带 X-XSRF-TOKEN |
|
XSS |
注入恶意脚本 |
避免拼接 HTML,使用 .text() 而非 .html() |
CSRF Token 处理
从 Cookie 或 Meta 标签获取 Token 并放入请求头
var csrfToken = $('meta[name="csrf-token"]').attr('content');
$.ajaxSetup({
headers: {
'X-XSRF-TOKEN': csrfToken /* 全局配置,每个请求自动带上 */
}
});
XSS 防御示例
var userInput = '<script>alert("恶意代码")</script>';
/* 危险写法:直接拼接 HTML,会执行脚本 */
// $('#content').html(userInput);
/* 安全写法:转义为文本,只展示不执行 */
$('#content').text(userInput);
/* 输出纯文本: <script>alert("恶意代码")</script> */
CSRF 就像是伪造的银行转账单,攻击者利用你已经登录的状态,偷偷提交了转账申请。
防御的办法就是在每一张真的转账单上盖一个特殊的、随机的“骑缝章”,这个章只有你自己知道,攻击者伪造的假单子上没有这个章,银行就会拒绝受理。
5.4.3. 历史遗留:JSONP 原理回顾(现代开发已弃用)
在 CORS 标准普及之前,JSONP 是实现跨域的主流手段。
|
特性 |
JSONP |
CORS (现代标准) |
|
核心原理 |
利用 <script> 标签 |
浏览器原生支持 |
|
请求方式 |
仅支持 GET |
支持所有 HTTP 方法 |
|
错误处理 |
极弱,无法捕获 404/500 |
完善的状态码处理 |
|
安全性 |
较低,存在被劫持风险 |
较高,支持预检机制 |
|
现代地位 |
已弃用 |
主流标准 |
它利用 <script> 标签不受同源策略限制的特性,通过动态创建标签请求 JSON 数据。
虽然现代开发已很少使用,但在维护老旧系统时仍需了解其原理。
jQuery 的 JSONP 调用方式
$.ajax({
url: 'https://api.legacy.com/data',
dataType: 'jsonp', /* 声明为 JSONP 模式 */
jsonpCallback: 'handleResponse', /* 指定回调函数名 */
success: function(data) {
console.log('收到数据:', data);
}
});
/* 底层原理模拟 */
function loadJSONP(url) {
var script = document.createElement('script');
script.src = url + '?callback=handleResponse';
document.body.appendChild(script); /* 插入页面即发起请求 */
}
/* 服务器返回:handleResponse({ "data": "…" }); */
JSONP 就像是“走私”通道。
正规的 HTTP 请求关卡重重,而 JSONP 利用 <script> 标签这个不受检查的“绿色通道”把数据运进来。
它的形式必须是 callback(data),就像是一辆必须贴着特定标签的车才能通过。
现在海关升级了,正规通道开放且更安全,走私通道自然就没人走了。





