AJAX (Asynchronous JavaScript and XML) 封装是为了简化重复的异步请求代码,提高开发效率和代码复用性。下面我将介绍几种常见的 AJAX 封装方式。
XMLHttpRequest。其主要特点如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
/** * 基于原生XHR的AJAX封装 * @param {Object} options 配置对象 * @param {string} options.url 请求地址 * @param {string} [options.method='GET'] 请求方法 * @param {Object} [options.data=null] 请求数据 * @param {Object} [options.headers={}] 请求头 * @param {function} [options.success] 成功回调 * @param {function} [options.error] 失败回调 */ function ajax(options) { const xhr = new XMLHttpRequest(); const method = options.method || 'GET'; let url = options.url; let data = options.data || null;
// 处理GET请求的查询参数 if (method === 'GET' && data) { const params = new URLSearchParams(); for (const key in data) { params.append(key, data[key]); } url += '?' + params.toString(); data = null; }
xhr.open(method, url, true);
// 设置请求头 if (options.headers) { for (const key in options.headers) { xhr.setRequestHeader(key, options.headers[key]); } }
xhr.onreadystatechange = function() { if (xhr.readyState === 4) { if (xhr.status >= 200 && xhr.status < 300) { let response = xhr.responseText; try { response = JSON.parse(response); } catch (e) {} options.success && options.success(response); } else { options.error && options.error(xhr.status, xhr.statusText); } } };
xhr.onerror = function() { options.error && options.error(-1, 'Network Error'); };
// 发送请求 if (data && typeof data === 'object') { xhr.setRequestHeader('Content-Type', 'application/json'); xhr.send(JSON.stringify(data)); } else { xhr.send(data); } }
// 使用示例 ajax({ url: '/api/user', method: 'POST', data: { name: 'John', age: 30 }, headers: { 'Authorization': 'Bearer token123' }, success: function(response) { console.log('Success:', response); }, error: function(status, statusText) { console.error('Error:', status, statusText); } }); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
/** * 基于Fetch API的AJAX封装 * @param {string} url 请求地址 * @param {Object} [options={}] 请求配置 * @returns {Promise} 返回Promise对象 */ function fetchAjax(url, options = {}) { const defaultOptions = { method: 'GET', headers: { 'Content-Type': 'application/json' }, credentials: 'same-origin', // 携带cookie ...options };
// 处理GET请求的查询参数 if (defaultOptions.method === 'GET' && defaultOptions.body) { const params = new URLSearchParams(); for (const key in defaultOptions.body) { params.append(key, defaultOptions.body[key]); } url += '?' + params.toString(); delete defaultOptions.body; }
// 处理非GET请求的body数据 if (defaultOptions.body && typeof defaultOptions.body === 'object') { defaultOptions.body = JSON.stringify(defaultOptions.body); }
return fetch(url, defaultOptions) .then(async response => { const data = await response.json().catch(() => ({})); if (!response.ok) { const error = new Error(response.statusText); error.response = response; error.data = data; throw error; } return data; }); }
// 使用示例 fetchAjax('/api/user', { method: 'POST', body: { name: 'John', age: 30 }, headers: { 'Authorization': 'Bearer token123' } }) .then(data => console.log('Success:', data)) .catch(err => console.error('Error:', err)); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
class Ajax { constructor(baseURL = '', timeout = 10000) { this.baseURL = baseURL; this.timeout = timeout; this.interceptors = { request: [], response: [] }; }
request(config) { // 处理请求拦截器 let chain = [this._dispatchRequest, undefined]; this.interceptors.request.forEach(interceptor => { chain.unshift(interceptor.fulfilled, interceptor.rejected); }); this.interceptors.response.forEach(interceptor => { chain.push(interceptor.fulfilled, interceptor.rejected); });
let promise = Promise.resolve(config); while (chain.length) { promise = promise.then(chain.shift(), chain.shift()); } return promise; }
_dispatchRequest(config) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); let url = config.baseURL ? config.baseURL + config.url : config.url; let data = config.data;
// 处理GET请求参数 if (config.method === 'GET' && data) { const params = new URLSearchParams(); for (const key in data) { params.append(key, data[key]); } url += '?' + params.toString(); data = null; }
xhr.timeout = config.timeout || 10000; xhr.open(config.method, url, true);
// 设置请求头 if (config.headers) { for (const key in config.headers) { xhr.setRequestHeader(key, config.headers[key]); } }
xhr.onload = function() { if (xhr.status >= 200 && xhr.status < 300) { let response = xhr.responseText; try { response = JSON.parse(response); } catch (e) {} resolve({ data: response, status: xhr.status, statusText: xhr.statusText, headers: xhr.getAllResponseHeaders() }); } else { reject(new Error(`Request failed with status code ${xhr.status}`)); } };
xhr.onerror = function() { reject(new Error('Network Error')); };
xhr.ontimeout = function() { reject(new Error('Timeout')); };
// 发送请求 if (data && typeof data === 'object') { xhr.setRequestHeader('Content-Type', 'application/json'); xhr.send(JSON.stringify(data)); } else { xhr.send(data); } }); }
get(url, config = {}) { return this.request({ ...config, method: 'GET', url }); }
post(url, data, config = {}) { return this.request({ ...config, method: 'POST', url, data }); }
// 添加拦截器 useRequestInterceptor(fulfilled, rejected) { this.interceptors.request.push({ fulfilled, rejected }); return this.interceptors.request.length - 1; }
useResponseInterceptor(fulfilled, rejected) { this.interceptors.response.push({ fulfilled, rejected }); return this.interceptors.response.length - 1; }
// 移除拦截器 ejectRequestInterceptor(id) { if (this.interceptors.request[id]) { this.interceptors.request.splice(id, 1); } }
ejectResponseInterceptor(id) { if (this.interceptors.response[id]) { this.interceptors.response.splice(id, 1); } } }
// 使用示例 const api = new Ajax('https://api.example.com');
// 添加请求拦截器 api.useRequestInterceptor(config => { config.headers = config.headers || {}; config.headers['Authorization'] = 'Bearer token123'; return config; });
// 添加响应拦截器 api.useResponseInterceptor(response => { console.log('Response:', response); return response.data; }, error => { console.error('Error:', error); return Promise.reject(error); });
// 发起请求 api.get('/user/123') .then(data => console.log('User data:', data)) .catch(err => console.error('Error:', err));
api.post('/user', { name: 'John', age: 30 }) .then(data => console.log('Created user:', data)) .catch(err => console.error('Error:', err)); |
统一接口:提供一致的调用方式,如get(), post()等方法
参数处理:
GET请求自动拼接查询参数
POST请求自动处理Content-Type
拦截器机制:支持请求/响应拦截
错误处理:统一错误处理逻辑
Promise支持:返回Promise便于链式调用
超时处理:设置合理的请求超时时间
扩展性:支持自定义配置和拦截器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
function withRetry(fn, retries = 3, delay = 1000) { return function(...args) { return new Promise((resolve, reject) => { function attempt(retryCount) { fn(...args) .then(resolve) .catch(err => { if (retryCount < retries) { setTimeout(() => attempt(retryCount + 1), delay); } else { reject(err); } }); } attempt(0); }); }; }
// 使用示例 const ajaxWithRetry = withRetry(ajax, 3, 1000); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
function createCancelToken() { let cancel; const token = new Promise((resolve, reject) => { cancel = reject; }); return { token, cancel }; }
// 在请求中检查取消token function ajaxWithCancel(options) { const { token, cancel } = createCancelToken(); const xhr = new XMLHttpRequest();
const promise = new Promise((resolve, reject) => { // ...正常请求逻辑
// 检查取消 token.catch(err => { xhr.abort(); reject(err); }); });
return { promise, cancel }; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
const cache = new Map();
function cachedAjax(options) { const cacheKey = JSON.stringify(options);
if (cache.has(cacheKey)) { return Promise.resolve(cache.get(cacheKey)); }
return ajax(options).then(response => { cache.set(cacheKey, response); return response; }); } |
根据项目需求选择合适的封装方式,小型项目可使用简单封装,大型项目建议使用成熟的库如Axios。