jquery-ajax.js 说明文档
概述
jquery-ajax.js 是一个完全独立 的 AJAX 封装库,API 与 jQuery 的 AJAX 方法100% 兼容。它只包含 AJAX 相关的功能,不包含 jQuery 的 DOM 操作、事件处理、动画等其他模块。
核心特点:
- 📦 零依赖 --- 纯原生 JavaScript(基于
XMLHttpRequest),不需要引入 jQuery - 🔄 API 兼容 ---
$.ajax()/$.get()/$.post()等方法和 jQuery 用法完全一致 - 💡 轻量 --- 仅约 13 KB(未压缩),约 460 行代码
- ✅ Promise 支持 ---
.done()/.fail()/.always()/.then()链式调用 - 🔧 全局配置 ---
$.ajaxSetup()统一管理默认参数 - 🏷️ 命名空间保护 --- 内置
$.noConflict(),不污染$符号
适用场景:
- 不需要 jQuery,但习惯 jQuery AJAX 风格的开发者
- 需要轻量级 AJAX 工具的项目
- 不想引入整个 jQuery 但想用熟悉的
$.ajax()语法的场景
快速开始
引入
html
<script src="jquery-ajax.js"></script>
引入后,全局 $ 对象上即拥有所有 AJAX 方法。
第一个请求
javascript
$.get('/api/users', function(data) {
console.log('用户列表:', data);
});
API 参考
$.ajax(settings)
核心方法,所有其他快捷方法最终都调用它。
参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
url |
String | '' |
请求地址 |
method / type |
String | 'GET' |
HTTP 方法(GET / POST / PUT / DELETE / PATCH) |
data |
Object / String | null |
发送到服务器的数据。对象会自动序列化为 query string |
dataType |
String | 'text' |
期望的响应类型:text / json / xml / html / script |
contentType |
String | 'application/x-www-form-urlencoded; charset=UTF-8' |
请求体的 Content-Type |
async |
Boolean | true |
是否异步请求 |
timeout |
Number | 0 |
超时时间(毫秒),0 表示不设超时 |
cache |
Boolean | true |
GET 请求是否缓存。设为 false 会追加 _=时间戳 参数 |
processData |
Boolean | true |
是否将 data 对象自动转换为 query string |
headers |
Object | {} |
自定义请求头键值对 |
beforeSend |
Function | null |
请求发送前的回调。接收 xhr 参数,返回 false 可取消请求 |
success |
Function | null |
请求成功时的回调。参数:data, status, xhr |
error |
Function | null |
请求失败时的回调。参数:xhr, status, errorThrown |
complete |
Function | null |
请求完成时的回调(无论成功或失败)。参数:xhr, status |
返回值
返回一个类 Promise 对象,支持以下方法:
.done(function(data, status, xhr) {})--- 成功时调用.fail(function(xhr, status, errorThrown) {})--- 失败时调用.always(function() {})--- 完成时始终调用.then(doneFn, failFn)--- done + fail 的语法糖.abort()--- 手动取消请求
基础示例
javascript
// 完整配置示例
$.ajax({
url: '/api/users',
method: 'POST',
data: {
name: 'Alice',
age: 28
},
dataType: 'json',
timeout: 5000,
headers: {
'Authorization': 'Bearer token123'
},
beforeSend: function(xhr) {
console.log('请求即将发送...');
// return false; // 取消请求
},
success: function(data, status, xhr) {
console.log('成功:', data);
},
error: function(xhr, status, error) {
console.error('失败:', status, error);
},
complete: function(xhr, status) {
console.log('请求结束, 状态:', status);
}
});
Promise 风格
javascript
// 使用 .done / .fail / .always
$.ajax({
url: '/api/config',
dataType: 'json'
})
.done(function(data) {
console.log('配置:', data);
})
.fail(function(xhr, status, err) {
console.error('加载配置失败', err);
})
.always(function() {
console.log('请求结束');
});
// 链式调用
$.ajax({ url: '/api/user/1', dataType: 'json' })
.then(function(user) {
// 第一个请求成功后,发起第二个请求
return $.ajax({ url: '/api/orders?userId=' + user.id });
})
.done(function(orders) {
console.log('订单:', orders);
})
.fail(function(err) {
console.error('出错了', err);
});
手动取消请求
javascript
var req = $.ajax({
url: '/api/long-task',
success: function(data) {
console.log('完成');
}
});
// 2 秒后取消
setTimeout(function() {
req.abort();
console.log('请求已取消');
}, 2000);
发送 JSON 数据
javascript
$.ajax({
url: '/api/users',
method: 'POST',
contentType: 'application/json',
processData: false, // 不自动转 query string
data: JSON.stringify({ // 手动序列化
name: 'Alice',
role: 'admin'
}),
dataType: 'json',
success: function(data) {
console.log('用户已创建:', data);
}
});
文件上传(配合 FormData)
javascript
var formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('category', 'image');
$.ajax({
url: '/api/upload',
method: 'POST',
data: formData,
contentType: false, // 让浏览器自动设置 multipart/form-data
processData: false, // 不对 FormData 做序列化
success: function(data) {
console.log('上传成功:', data);
}
});
$.get(url , data , success , dataType)
GET 请求快捷方法。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
url |
String | ✅ | 请求地址 |
data |
Object / String | ❌ | 发送的数据(拼接到 URL 上) |
success |
Function | ❌ | 成功回调 function(data, status, xhr) |
dataType |
String | ❌ | 响应类型 |
javascript
// 最简形式
$.get('/api/users');
// 带参数
$.get('/api/users', { page: 2, limit: 20 });
// 带回调
$.get('/api/users', function(data) {
console.log(data);
});
// 带参数 + 回调
$.get('/api/users', { page: 2 }, function(data) {
console.log(data);
});
// 指定响应类型
$.get('/api/users', { page: 2 }, function(data) {
console.log(data);
}, 'json');
// Promise 风格(无 success 回调)
$.get('/api/users', { page: 1 })
.done(function(data) { console.log(data); })
.fail(function(xhr, status, err) { console.error(err); });
$.post(url , data , success , dataType)
POST 请求快捷方法。参数签名与 $.get() 完全一致。
javascript
// 表单提交
$.post('/api/login', {
username: 'admin',
password: '123456'
}, function(response) {
if (response.success) {
console.log('登录成功');
}
}, 'json');
// Promise 风格
$.post('/api/login', {
username: 'admin',
password: '123456'
})
.done(function(data) {
console.log('登录成功', data);
})
.fail(function() {
console.log('登录失败');
});
$.getJSON(url , data , success)
获取 JSON 数据的快捷方法,等价于 $.ajax({ dataType: 'json' })。
javascript
// 基础用法
$.getJSON('/api/config', function(config) {
console.log('站点名称:', config.siteName);
});
// 带查询参数
$.getJSON('/api/search', { keyword: 'jQuery', limit: 10 }, function(results) {
console.log('搜索结果:', results);
});
// Promise 风格
$.getJSON('/api/status')
.done(function(data) { console.log('服务状态:', data); })
.fail(function() { console.error('服务器不可用'); });
$.getScript(url , success)
动态加载并执行远程 JavaScript 文件。加载完成后可以立即使用文件中定义的变量和函数。
javascript
// 加载插件
$.getScript('/js/chart-plugin.js', function() {
console.log('图表插件加载完成');
// 此时可以使用插件中的方法
initChart();
});
// Promise 风格
$.getScript('/js/utils.js')
.done(function() {
console.log('工具库加载成功');
})
.fail(function() {
console.error('加载失败');
});
$.ajaxSetup(settings)
设置全局 AJAX 默认参数,对之后发出的所有请求生效。
javascript
// 全局设置
$.ajaxSetup({
timeout: 10000, // 统一超时 10 秒
dataType: 'json', // 统一期望 JSON 响应
cache: false, // 统一禁用 GET 缓存
headers: {
'X-Custom-Header': 'my-app'
},
beforeSend: function(xhr) {
// 自动附加 Token
var token = localStorage.getItem('auth_token');
if (token) {
xhr.setRequestHeader('Authorization', 'Bearer ' + token);
}
},
error: function(xhr) {
// 统一的 401 处理
if (xhr.status === 401) {
window.location.href = '/login';
}
}
});
// 设置之后的请求自动沿用这些默认值
$.get('/api/users'); // 自动 JSON 解析、自动带 Token
$.post('/api/save', { ... }); // 同样沿用
注意: $.ajaxSetup() 影响全局,不建议在插件或公共库中使用,可能会干扰页面中其他代码的 AJAX 行为。
$.param(obj)
将 JavaScript 对象序列化为 URL 编码的参数字符串,等价于 jQuery 的 $.param()。
$.param(object) → string
javascript
// 简单对象
$.param({ name: 'Alice', age: 28 });
// → "name=Alice&age=28"
// 嵌套对象
$.param({ user: { name: 'Alice', role: 'admin' } });
// → "user%5Bname%5D=Alice&user%5Brole%5D=admin"
// 解码后: "user[name]=Alice&user[role]=admin"
// 数组
$.param({ tags: ['js', 'html', 'css'] });
// → "tags%5B%5D=js&tags%5B%5D=html&tags%5B%5D=css"
// 解码后: "tags[]=js&tags[]=html&tags[]=css"
// 复杂嵌套
$.param({
search: 'keyword',
filters: { category: 'book', price: { min: 10, max: 50 } }
});
// → "search=keyword&filters%5Bcategory%5D=book&filters%5Bprice%5D%5Bmin%5D=10&filters%5Bprice%5D%5Bmax%5D=50"
$.noConflict(removeAll)
当页面中同时存在多个使用 $ 的库时,释放对 $ 的控制权。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
removeAll |
Boolean | false |
是否同时释放 jQuery 变量 |
javascript
// 场景:页面先引入了本库,又引入了完整 jQuery
// <script src="jquery-ajax.js"></script> ← 本库占用了 $
// <script src="jquery-3.7.1.min.js"></script> ← 完整 jQuery 也想要 $
var $ajax = $.noConflict(); // 释放 $ 还给 jQuery
// 之后:
// $ → 完整 jQuery
// $ajax → 本库(AJAX 功能)
// jQAjax → 本库(备选命名空间,始终可用)
// 如果还想释放 jQuery 变量
var $ajax = $.noConflict(true);
与 jQuery AJAX 的 API 兼容对照表
| 功能 | jQuery | jquery-ajax.js | 说明 |
|---|---|---|---|
| 核心 AJAX | $.ajax() |
$.ajax() |
✅ 完全兼容 |
| GET 请求 | $.get() |
$.get() |
✅ 完全兼容 |
| POST 请求 | $.post() |
$.post() |
✅ 完全兼容 |
| JSON 请求 | $.getJSON() |
$.getJSON() |
✅ 完全兼容 |
| 动态加载 JS | $.getScript() |
$.getScript() |
✅ 完全兼容 |
| 全局配置 | $.ajaxSetup() |
$.ajaxSetup() |
✅ 完全兼容 |
| 参数序列化 | $.param() |
$.param() |
✅ 完全兼容 |
| 命名空间释放 | $.noConflict() |
$.noConflict() |
✅ 完全兼容 |
| Promise (done/fail/always) | ✅ | ✅ | 完全兼容 |
| 请求取消 | .abort() |
.abort() |
完全兼容 |
| beforeSend 回调 | ✅ | ✅ | 完全兼容 |
| JSONP | dataType: 'jsonp' |
❌ | 未实现(现代项目使用 CORS) |
| 全局 AJAX 事件 | $(document).ajaxStart() |
❌ | 未实现(可通过 ajaxSetup 模拟) |
| statusCode 回调 | statusCode: {404: fn} |
❌ | 未实现 |
| converters | $.ajaxSetup({ converters }) |
❌ | 未实现 |
结论: 日常开发中 95% 的 AJAX 场景完全兼容。
迁移指南
从 jQuery 迁移到 jquery-ajax.js
如果你的项目只使用了 jQuery 的 AJAX 功能,切换到本库只需两步:
- 替换 script 标签:
html
<!-- 原来 -->
<script src="jquery-3.7.1.min.js"></script>
<!-- 改为 -->
<script src="jquery-ajax.js"></script>
- 代码无需修改 --- 零改动迁移: 所有
$.ajax/$.get/$.post/$.getJSON/$.getScript/$.ajaxSetup/$.param调用保持不变。
与完整 jQuery 共存
如果页面中已经引入了完整 jQuery,且你也想使用本库的 AJAX 方法:
html
<!-- 先引入完整 jQuery($ 被 jQuery 占用)-->
<script src="jquery-3.7.1.min.js"></script>
<!-- 再引入本库 -->
<script src="jquery-ajax.js"></script>
<script>
// $ 仍然是完整 jQuery
$('#myDiv').hide(); // jQuery DOM 操作
// 用 jQAjax 访问本库的 AJAX 方法
jQAjax.get('/api/data');
jQAjax.ajax({ url: '/api/users' });
// 或者立即释放 $ 让给 jQuery,用变量保存本库引用
var $ajax = $.noConflict();
// $ajax 就是本库
</script>
dataType 详解
| dataType | 响应处理方式 | 传给 success 的值 |
|---|---|---|
'text'(默认) |
原样返回 | 字符串 |
'json' |
JSON.parse() 解析 |
JavaScript 对象 / 数组 |
'xml' |
读取 xhr.responseXML |
XML Document |
'html' |
同 text | HTML 字符串 |
'script' |
eval() 执行脚本内容 |
脚本文本 |
请求流程与回调执行顺序
$.ajax() 被调用
│
├─ 1. 合并 settings(默认 → 全局 → 当前)
├─ 2. 处理 data 序列化
├─ 3. 创建 XMLHttpRequest
├─ 4. 设置请求头
│
├─ 5. beforeSend(xhr) ← 可以在这里取消请求
│
├─ 6. xhr.send()
│
├─ 7. 等待响应...
│
├─ 8. 响应到达:
│ ├─ 成功 (2xx / 304):
│ │ ├─ 按 dataType 转换数据
│ │ ├─ success(data, 'success', xhr)
│ │ └─ .done(data, 'success', xhr)
│ │
│ └─ 失败:
│ ├─ error(xhr, 'error', errorThrown)
│ └─ .fail(xhr, 'error', errorThrown)
│
└─ 9. complete(xhr, status) ← 无论成功失败
.always()
常见用法场景
场景一:RESTful API 调用
javascript
// 查询列表
$.get('/api/articles', { page: 1, limit: 20 }, function(data) {
renderList(data.items);
});
// 创建
$.ajax({
url: '/api/articles',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify({ title: '新文章', content: '...' }),
dataType: 'json',
success: function(article) {
console.log('已创建, ID:', article.id);
}
});
// 更新
$.ajax({
url: '/api/articles/42',
method: 'PUT',
contentType: 'application/json',
data: JSON.stringify({ title: '修改后的标题' }),
success: function() { console.log('已更新'); }
});
// 删除
$.ajax({
url: '/api/articles/42',
method: 'DELETE',
success: function() { console.log('已删除'); }
});
场景二:统一错误处理 + Loading 指示器
javascript
// 全局配置
$.ajaxSetup({
timeout: 10000,
beforeSend: function() {
$('#loading').show();
},
complete: function() {
$('#loading').hide();
},
error: function(xhr) {
if (xhr.status === 401) {
alert('登录已过期,请重新登录');
window.location.href = '/login';
} else if (xhr.status === 403) {
alert('没有权限执行此操作');
} else if (xhr.status >= 500) {
alert('服务器错误,请稍后重试');
}
}
});
// 后续业务代码无需重复处理 Loading 和通用错误
$.get('/api/users', { page: 1 }, function(data) {
renderTable(data);
});
场景三:并发请求等待
javascript
// 同时发起多个请求,全部完成后再处理
var req1 = $.get('/api/users');
var req2 = $.get('/api/orders');
var req3 = $.get('/api/products');
var completed = 0;
var results = [];
function checkAll() {
completed++;
if (completed === 3) {
console.log('三个请求全部完成:', results);
}
}
req1.done(function(data) { results[0] = data; checkAll(); });
req2.done(function(data) { results[1] = data; checkAll(); });
req3.done(function(data) { results[2] = data; checkAll(); });
场景四:带重试的请求
javascript
function fetchWithRetry(url, maxRetries) {
maxRetries = maxRetries || 3;
var attempts = 0;
function attempt() {
return $.ajax({ url: url, dataType: 'json' }).fail(function(xhr, status, err) {
attempts++;
if (attempts < maxRetries) {
console.log('第 ' + attempts + ' 次重试...');
return attempt();
}
});
}
return attempt();
}
// 使用
fetchWithRetry('/api/unreliable-endpoint', 3)
.done(function(data) { console.log('成功:', data); })
.fail(function() { console.error('重试 3 次后仍失败'); });
技术实现说明
| 方面 | 实现方式 |
|---|---|
| 底层传输 | XMLHttpRequest(非 fetch),确保 beforeSend(xhr) 和 xhr.abort() 可用 |
| Promise 实现 | 自实现的 Deferred 模式(约 40 行),不依赖 ES6 Promise |
| 参数序列化 | 自实现的 $.param(),支持嵌套对象和数组,与 jQuery 行为一致 |
| 命名空间 | 同时暴露 $ / jQuery / jQAjax 三个变量 |
| 模块封装 | IIFE 模式,私有变量不污染全局 |
文件信息
jquery-ajax.js --- 主库文件(460 行,约 13 KB)
jquery-ajax-说明文档.md --- 本说明文件
无需任何构建工具,直接在浏览器中通过 <script> 标签引入即可使用。
javascript
/*!
* jquery-ajax.js v1.0.0
* 独立的 AJAX 封装库,API 与 jQuery AJAX 完全一致,不依赖 jQuery。
* 适用于只想使用 $.ajax / $.get / $.post 等 AJAX 方法,而不想引入整个 jQuery 的场景。
*
* 包含方法:
* $.ajax() - 核心 AJAX 方法
* $.get() - GET 请求
* $.post() - POST 请求
* $.getJSON() - 获取 JSON 数据
* $.getScript() - 动态加载并执行 JS 文件
* $.ajaxSetup() - 全局默认设置
* $.param() - 对象序列化为 URL 参数字符串
* $.noConflict() - 释放 $ 控制权
*
* 使用方式:
* <script src="jquery-ajax.js"></script>
* 然后和 jQuery 一样使用 $ 即可(仅限 AJAX 相关方法)。
*/
;(function(global, undefined) {
'use strict';
// ==================== 内部工具 ====================
// 获取值的类型字符串
function type(obj) {
if (obj === null) return 'null';
if (obj === undefined) return 'undefined';
var t = Object.prototype.toString.call(obj);
return t.slice(8, -1).toLowerCase();
}
// 浅合并(后面的覆盖前面的)
function extend(target) {
var i = 1, len = arguments.length, src, key;
for (; i < len; i++) {
src = arguments[i];
if (src != null) {
for (key in src) {
if (src.hasOwnProperty(key)) {
target[key] = src[key];
}
}
}
}
return target;
}
// 深度合并
function extendDeep() {
var target = arguments[0] || {}, i = 1, len = arguments.length,
options, name, src, copy;
for (; i < len; i++) {
options = arguments[i];
if (options != null) {
for (name in options) {
src = target[name];
copy = options[name];
if (copy !== undefined) {
target[name] = copy;
}
}
}
}
return target;
}
// 判断是否为纯对象(PlainObject)
function isPlainObject(obj) {
if (type(obj) !== 'object' || obj.nodeType || obj === global) {
return false;
}
try {
return obj.constructor &&
Object.prototype.hasOwnProperty.call(obj.constructor.prototype, 'isPrototypeOf');
} catch (e) {
return false;
}
}
// ==================== $.param() ====================
function buildParams(prefix, obj, add) {
if (type(obj) === 'array') {
// 数组
for (var i = 0, len = obj.length; i < len; i++) {
var val = obj[i];
if (type(val) === 'array' || isPlainObject(val)) {
buildParams(prefix + '[' + i + ']', val, add);
} else {
add(prefix + '[]', val);
}
}
} else if (isPlainObject(obj)) {
// 对象
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
var subVal = obj[key];
var subPrefix = prefix ? prefix + '[' + key + ']' : key;
if (type(subVal) === 'array' || isPlainObject(subVal)) {
buildParams(subPrefix, subVal, add);
} else {
add(subPrefix, subVal);
}
}
}
} else {
add(prefix, obj);
}
}
function param(obj) {
var parts = [];
function add(key, value) {
if (value === undefined || value === null) {
value = '';
}
parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
}
if (type(obj) === 'array') {
for (var i = 0, len = obj.length; i < len; i++) {
var item = obj[i];
if (item && item.name && item.value !== undefined) {
// serializeArray 格式
add(item.name, item.value);
}
}
} else {
buildParams('', obj, add);
}
return parts.join('&').replace(/%20/g, '+');
}
// ==================== Deferred / Promise ====================
function createDeferred() {
var _done = [], _fail = [], _always = [];
var _state = 'pending';
var _args_done, _args_fail;
var promise = {
done: function(cb) {
if (_state === 'resolved') { cb.apply(null, _args_done); }
else if (_state === 'pending') { _done.push(cb); }
return this;
},
fail: function(cb) {
if (_state === 'rejected') { cb.apply(null, _args_fail); }
else if (_state === 'pending') { _fail.push(cb); }
return this;
},
always: function(cb) {
if (_state !== 'pending') { cb(); }
else { _always.push(cb); }
return this;
},
then: function(doneFn, failFn) {
if (doneFn) this.done(doneFn);
if (failFn) this.fail(failFn);
return this;
},
state: function() { return _state; },
promise: function() { return promise; }
};
var deferred = {
resolve: function() {
if (_state !== 'pending') return;
_state = 'resolved';
_args_done = Array.prototype.slice.call(arguments);
for (var i = 0; i < _done.length; i++) _done[i].apply(null, _args_done);
for (var j = 0; j < _always.length; j++) _always[j]();
},
reject: function() {
if (_state !== 'pending') return;
_state = 'rejected';
_args_fail = Array.prototype.slice.call(arguments);
for (var i = 0; i < _fail.length; i++) _fail[i].apply(null, _args_fail);
for (var j = 0; j < _always.length; j++) _always[j]();
},
notify: function() {},
promise: promise
};
return deferred;
}
// ==================== AJAX 核心 ====================
// 默认配置
var ajaxDefaults = {
url: '',
method: 'GET',
type: 'GET', // method 的别名
data: null, // 发送的数据(对象或字符串)
dataType: 'text', // 期望的响应类型: text, json, xml, html, script
contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
async: true,
timeout: 0,
cache: true,
processData: true, // 自动将 data 对象转换为 query string
headers: {},
beforeSend: null, // function(xhr) --- return false 可取消请求
success: null, // function(data, status, xhr)
error: null, // function(xhr, status, errorThrown)
complete: null // function(xhr, status)
};
// 全局默认设置存储
var globalSettings = {};
/**
* $.ajax(settings)
* 核心 AJAX 方法,返回一个类 Promise 对象(支持 .done / .fail / .always / .then / .abort)。
*/
function ajax(options) {
var settings = extend({}, ajaxDefaults, globalSettings, options);
// method / type 兼容
settings.method = (settings.method || settings.type || 'GET').toUpperCase();
// ---- 处理 data ----
if (settings.processData && settings.data && typeof settings.data === 'object') {
settings.data = param(settings.data);
}
// ---- 处理 cache(GET 请求禁用缓存时追加时间戳)----
if (settings.method === 'GET' && settings.cache === false) {
var sep = settings.url.indexOf('?') > -1 ? '&' : '?';
settings.url += sep + '_=' + Date.now();
}
// ---- 处理 GET 请求:data 拼接到 URL ----
if (settings.method === 'GET' && settings.data && typeof settings.data === 'string') {
var sep2 = settings.url.indexOf('?') > -1 ? '&' : '?';
settings.url += sep2 + settings.data;
settings.data = null;
}
// ---- 创建 Deferred 和 XHR ----
var deferred = createDeferred();
var xhr = new XMLHttpRequest();
xhr.open(settings.method, settings.url, settings.async);
// 设置 Content-Type(GET 请求不需要)
if (settings.contentType && settings.method !== 'GET') {
xhr.setRequestHeader('Content-Type', settings.contentType);
}
// 自定义请求头
for (var key in settings.headers) {
if (settings.headers.hasOwnProperty(key)) {
xhr.setRequestHeader(key, settings.headers[key]);
}
}
// 超时
if (settings.timeout > 0) {
xhr.timeout = settings.timeout;
}
// ---- beforeSend ----
if (typeof settings.beforeSend === 'function') {
if (settings.beforeSend(xhr) === false) {
// 取消请求
deferred.reject(xhr, 'abort', 'Request cancelled by beforeSend');
return deferred.promise;
}
}
// ---- 响应处理 ----
xhr.onreadystatechange = function() {
if (xhr.readyState !== 4) return;
var status = xhr.status;
var responseText = xhr.responseText;
var responseData = responseText;
var isSuccess = (status >= 200 && status < 300) || status === 304 || status === 0;
// 根据 dataType 转换响应数据
if (isSuccess) {
switch (settings.dataType) {
case 'json':
try {
responseData = JSON.parse(responseText);
} catch (e) {
responseData = null;
isSuccess = false;
}
break;
case 'xml':
responseData = xhr.responseXML;
break;
case 'script':
try {
global.eval.call(global, responseText);
} catch (e) {
// eval 失败
}
responseData = responseText;
break;
case 'html':
case 'text':
default:
responseData = responseText;
break;
}
}
if (isSuccess) {
// 成功
if (typeof settings.success === 'function') {
settings.success(responseData, 'success', xhr);
}
deferred.resolve(responseData, 'success', xhr);
} else {
// 失败
var errorMsg = xhr.statusText || 'Unknown Error';
if (typeof settings.error === 'function') {
settings.error(xhr, 'error', errorMsg);
}
deferred.reject(xhr, 'error', errorMsg);
}
// complete 回调
if (typeof settings.complete === 'function') {
settings.complete(xhr, isSuccess ? 'success' : 'error');
}
// always 回调通过 deferred.always 触发(在 resolve/reject 中已处理)
};
// 发送请求
var body = (settings.method === 'GET') ? null : (settings.data || null);
xhr.send(body);
// 附加 abort 方法到 promise
deferred.promise.abort = function() {
xhr.abort();
};
return deferred.promise;
}
// ==================== 快捷方法 ====================
/**
* $.get(url [, data] [, success] [, dataType])
*/
function get(url, data, success, dataType) {
if (typeof data === 'function') {
dataType = dataType || success;
success = data;
data = undefined;
}
return ajax({
url: url,
method: 'GET',
data: data,
success: success,
dataType: dataType
});
}
/**
* $.post(url [, data] [, success] [, dataType])
*/
function post(url, data, success, dataType) {
if (typeof data === 'function') {
dataType = dataType || success;
success = data;
data = undefined;
}
return ajax({
url: url,
method: 'POST',
data: data,
success: success,
dataType: dataType
});
}
/**
* $.getJSON(url [, data] [, success])
*/
function getJSON(url, data, success) {
if (typeof data === 'function') {
success = data;
data = undefined;
}
return ajax({
url: url,
method: 'GET',
data: data,
success: success,
dataType: 'json'
});
}
/**
* $.getScript(url [, success])
* 动态加载并执行 JavaScript 文件
*/
function getScript(url, success) {
return ajax({
url: url,
method: 'GET',
dataType: 'script',
cache: true,
success: success
});
}
// ==================== $.ajaxSetup() ====================
/**
* $.ajaxSetup(settings)
* 设置全局 AJAX 默认参数,对之后所有请求生效。
*/
function ajaxSetup(options) {
extend(globalSettings, options);
}
// ==================== 构建 $ 命名空间 ====================
var _$ = global.$; // 保存可能存在的旧 $
var _jQuery = global.jQuery; // 保存可能存在的旧 jQuery
var $ = {
ajax: ajax,
get: get,
post: post,
getJSON: getJSON,
getScript: getScript,
ajaxSetup: ajaxSetup,
param: param,
// 用于获取当前活跃的 AJAX 请求数(简化版,始终返回 0,仅为 API 占位)
active: 0,
/**
* $.noConflict(removeAll)
* 释放 $ 控制权,返回当前库的引用。
* removeAll: 是否同时释放 jQuery 变量
*/
noConflict: function(removeAll) {
global.$ = _$;
if (removeAll) {
global.jQuery = _jQuery;
}
return $;
}
};
// 暴露到全局
global.$ = $;
global.jQuery = $; // 同时暴露 jQuery 变量
global.jQAjax = $; // 备用命名空间,避免与完整 jQuery 冲突
})(window);