一、AJAX 乱码问题
1.1 原因分析
AJAX 乱码通常由以下原因引起:
字符编码不一致
- 服务器响应编码与前端解析编码不匹配
- 请求/响应头未正确设置字符集
数据传输过程编码丢失
- GET请求参数在URL中传递时的编码问题
- POST请求体编码问题
1.2 解决方案
// 1. 统一使用UTF-8编码
// 服务端设置(以Java为例)
response.setContentType("text/html;charset=UTF-8");
request.setCharacterEncoding("UTF-8");
// 2. AJAX请求设置请求头
$.ajax({
url: 'api/data',
type: 'POST',
contentType: 'application/x-www-form-urlencoded;charset=UTF-8',
data: { name: '张三' },
success: function(response) {
console.log(response);
}
});
// 3. 处理特殊字符
function encodeData(data) {
return encodeURIComponent(JSON.stringify(data));
}
二、AJAX 异步与同步
2.1 异步模式(默认)
// 异步示例
console.log('1. 开始请求');
$.ajax({
url: 'api/data',
async: true, // 默认true
success: function() {
console.log('3. 请求完成');
}
});
console.log('2. 继续执行');
// 输出顺序:1 → 2 → 3
2.2 同步模式
// 同步示例 - 会阻塞后续代码执行
console.log('1. 开始请求');
$.ajax({
url: 'api/data',
async: false, // 设置为同步
success: function() {
console.log('2. 请求完成');
}
});
console.log('3. 继续执行');
// 输出顺序:1 → 2 → 3(会等待请求完成)
2.3 使用建议
- 推荐使用异步:避免阻塞UI线程
- 使用Promise/async-await处理异步流程
// 使用Promise封装
function ajaxRequest(url) {
return new Promise((resolve, reject) => {
$.ajax({
url: url,
success: resolve,
error: reject
});
});
}
// async/await使用
async function fetchData() {
try {
const data = await ajaxRequest('api/data');
console.log('数据:', data);
} catch (error) {
console.error('请求失败:', error);
}
}
三、jQuery AJAX 封装实现
3.1 基础封装
// 简单封装示例
const MyAjax = {
// 默认配置
defaults: {
baseURL: '',
timeout: 10000,
headers: {
'Content-Type': 'application/json'
}
},
// 合并配置
mergeConfig(config) {
return { ...this.defaults, ...config };
},
// 请求方法
request(config) {
const mergedConfig = this.mergeConfig(config);
return new Promise((resolve, reject) => {
$.ajax({
url: mergedConfig.baseURL + mergedConfig.url,
type: mergedConfig.method || 'GET',
data: mergedConfig.data,
headers: mergedConfig.headers,
timeout: mergedConfig.timeout,
dataType: mergedConfig.dataType || 'json',
success: function(response) {
resolve(response);
},
error: function(xhr, status, error) {
reject({
status: xhr.status,
statusText: xhr.statusText,
message: error
});
},
// 请求完成回调
complete: function() {
console.log('请求完成');
}
});
});
},
// 快捷方法
get(url, params = {}) {
return this.request({
url: url,
method: 'GET',
data: params
});
},
post(url, data = {}) {
return this.request({
url: url,
method: 'POST',
data: JSON.stringify(data)
});
},
put(url, data = {}) {
return this.request({
url: url,
method: 'PUT',
data: JSON.stringify(data)
});
},
delete(url) {
return this.request({
url: url,
method: 'DELETE'
});
}
};
3.2 高级封装(支持拦截器)
class AjaxLibrary {
constructor() {
this.defaults = {
baseURL: '',
timeout: 10000,
headers: {},
withCredentials: false
};
// 拦截器
this.interceptors = {
request: [],
response: []
};
}
// 添加请求拦截器
useRequestInterceptor(interceptor) {
this.interceptors.request.push(interceptor);
return this;
}
// 添加响应拦截器
useResponseInterceptor(interceptor) {
this.interceptors.response.push(interceptor);
return this;
}
// 执行拦截器链
async runInterceptors(chain, initialValue) {
let value = initialValue;
for (const interceptor of chain) {
value = await interceptor(value);
}
return value;
}
// 核心请求方法
async request(config) {
// 合并配置
const mergedConfig = { ...this.defaults, ...config };
// 请求拦截
try {
mergedConfig = await this.runInterceptors(
this.interceptors.request,
mergedConfig
);
} catch (error) {
return Promise.reject(error);
}
// 发送请求
return new Promise((resolve, reject) => {
$.ajax({
url: mergedConfig.baseURL + mergedConfig.url,
type: mergedConfig.method || 'GET',
data: mergedConfig.data,
headers: mergedConfig.headers,
timeout: mergedConfig.timeout,
dataType: mergedConfig.dataType || 'json',
xhrFields: {
withCredentials: mergedConfig.withCredentials
},
beforeSend: function(xhr) {
// 可以在这里添加全局loading
console.log('开始发送请求');
},
success: async (response, status, xhr) => {
try {
// 响应拦截
const processedResponse = await this.runInterceptors(
this.interceptors.response,
{ data: response, status: xhr.status, headers: xhr.getAllResponseHeaders() }
);
resolve(processedResponse);
} catch (error) {
reject(error);
}
},
error: function(xhr, status, error) {
const err = {
status: xhr.status,
statusText: xhr.statusText,
message: error,
response: xhr.responseText
};
reject(err);
},
complete: function() {
// 可以在这里隐藏全局loading
console.log('请求完成');
}
});
});
}
// 创建实例方法
create(instanceConfig) {
const instance = new AjaxLibrary();
instance.defaults = { ...this.defaults, ...instanceConfig };
return instance;
}
}
// 使用示例
const api = new AjaxLibrary();
// 设置全局配置
api.defaults.baseURL = 'https://api.example.com';
api.defaults.headers['Authorization'] = 'Bearer token';
// 添加拦截器
api.useRequestInterceptor((config) => {
// 添加时间戳防止缓存
if (config.method === 'GET') {
config.url += (config.url.includes('?') ? '&' : '?') + '_t=' + Date.now();
}
return config;
});
api.useResponseInterceptor((response) => {
// 统一处理响应格式
if (response.status === 200) {
return response.data;
} else {
throw new Error('请求失败');
}
});
// 使用快捷方法
['get', 'post', 'put', 'delete', 'patch'].forEach(method => {
AjaxLibrary.prototype[method] = function(url, data, config) {
return this.request({
url,
method: method.toUpperCase(),
data,
...config
});
};
});
// 发送请求
api.get('/users')
.then(data => console.log(data))
.catch(error => console.error(error));
3.3 错误处理和重试机制
// 扩展错误处理和重试
class EnhancedAjax extends AjaxLibrary {
constructor(maxRetries = 3) {
super();
this.maxRetries = maxRetries;
}
async requestWithRetry(config, retryCount = 0) {
try {
return await super.request(config);
} catch (error) {
// 特定错误码重试
const shouldRetry = this.shouldRetry(error, retryCount);
if (shouldRetry) {
console.log(`第${retryCount + 1}次重试`);
return this.requestWithRetry(config, retryCount + 1);
}
throw error;
}
}
shouldRetry(error, retryCount) {
// 网络错误或5xx错误重试
const retryCodes = [0, 500, 502, 503, 504];
return retryCount < this.maxRetries &&
(retryCodes.includes(error.status) || !navigator.onLine);
}
// 并发请求控制
async all(requests, maxConcurrent = 5) {
const results = [];
const executing = [];
for (const request of requests) {
const promise = request().then(result => {
results.push(result);
executing.splice(executing.indexOf(promise), 1);
return result;
});
executing.push(promise);
if (executing.length >= maxConcurrent) {
await Promise.race(executing);
}
}
await Promise.all(executing);
return results;
}
}
四、完整使用示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AJAX封装示例</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body>
<div id="app">
<button onclick="getUsers()">获取用户</button>
<button onclick="createUser()">创建用户</button>
<div id="result"></div>
</div>
<script>
// 创建API实例
const api = new EnhancedAjax(3);
// 配置
api.defaults.baseURL = 'https://jsonplaceholder.typicode.com';
api.defaults.headers['Content-Type'] = 'application/json';
// 请求拦截器 - 添加认证token
api.useRequestInterceptor((config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
}
return config;
});
// 响应拦截器 - 统一处理错误
api.useResponseInterceptor((response) => {
if (response.status >= 400) {
throw new Error(`HTTP错误: ${response.status}`);
}
return response.data;
});
// 业务函数
async function getUsers() {
try {
const users = await api.get('/users');
document.getElementById('result').innerHTML =
JSON.stringify(users, null, 2);
} catch (error) {
console.error('获取用户失败:', error);
document.getElementById('result').innerHTML =
`错误: ${error.message}`;
}
}
async function createUser() {
const newUser = {
name: '张三',
email: 'zhangsan@example.com'
};
try {
const result = await api.post('/users', newUser);
alert(`创建成功! ID: ${result.id}`);
} catch (error) {
console.error('创建用户失败:', error);
}
}
// 并发请求示例
async function fetchMultipleData() {
const requests = [
() => api.get('/users/1'),
() => api.get('/posts/1'),
() => api.get('/comments/1')
];
const results = await api.all(requests, 2);
console.log('并发结果:', results);
}
</script>
</body>
</html>
五、最佳实践建议
编码规范
异步处理
- 使用Promise/async-await替代回调
- 避免同步请求阻塞UI
封装设计
性能优化
- 合理设置超时时间
- 实现请求重试机制
- 控制并发请求数
安全性
- CSRF token处理
- 请求参数验证
- HTTPS强制使用
这样的封装可以提高代码复用性,统一错误处理,简化AJAX调用,并提供了良好的扩展性。