AJAX常見的幾種封裝方法實(shí)例詳解
前言
AJAX (Asynchronous JavaScript and XML) 封裝是為了簡化重復(fù)的異步請求代碼,提高開發(fā)效率和代碼復(fù)用性。下面我將介紹幾種常見的 AJAX 封裝方式。
方法1. 基于原生 XMLHttpRequest 的封裝
XMLHttpRequest。其主要特點(diǎn)如下:
- 實(shí)現(xiàn)動態(tài)不刷新,通過異步?式,提升?戶體驗(yàn),優(yōu)化了瀏覽器和服務(wù)器之間的傳輸。
- 把?部分原本由服務(wù)器負(fù)擔(dān)的?作轉(zhuǎn)移到客戶端,利?客戶端閑置的資源進(jìn)?處理,減輕服務(wù)器和帶寬的負(fù)擔(dān),節(jié)約空間和成本。
- ?刷新更新??,?戶不?再像以前?樣在服務(wù)器處理數(shù)據(jù)時(shí),只能在死板的?屏前焦急的等待。AJAX使?XMLHttpRequest對象發(fā)送請求并得到服務(wù)器響應(yīng),在不需要重新載?整個(gè)??的情況下,就可以通過DOM及時(shí)將更新的內(nèi)容顯示在??上。
/** * 基于原生XHR的AJAX封裝 * @param {Object} options 配置對象 * @param {string} options.url 請求地址 * @param {string} [options.method='GET'] 請求方法 * @param {Object} [options.data=null] 請求數(shù)據(jù) * @param {Object} [options.headers={}] 請求頭 * @param {function} [options.success] 成功回調(diào) * @param {function} [options.error] 失敗回調(diào) */ function ajax(options) { const xhr = new XMLHttpRequest(); const method = options.method || 'GET'; let url = options.url; let data = options.data || null; // 處理GET請求的查詢參數(shù) 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); // 設(shè)置請求頭 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'); }; // 發(fā)送請求 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); } });
方法2. 基于 Fetch API 的封裝
/** * 基于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請求的查詢參數(shù) 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數(shù)據(jù) 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));
方法 3. 基于 Axios 風(fēng)格的封裝
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請求參數(shù) 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); // 設(shè)置請求頭 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')); }; // 發(fā)送請求 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; }); // 添加響應(yīng)攔截器 api.useResponseInterceptor(response => { console.log('Response:', response); return response.data; }, error => { console.error('Error:', error); return Promise.reject(error); }); // 發(fā)起請求 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));
4. 封裝要點(diǎn)總結(jié)
統(tǒng)一接口:提供一致的調(diào)用方式,如get(), post()等方法
參數(shù)處理:
GET請求自動拼接查詢參數(shù)
POST請求自動處理Content-Type
攔截器機(jī)制:支持請求/響應(yīng)攔截
錯(cuò)誤處理:統(tǒng)一錯(cuò)誤處理邏輯
Promise支持:返回Promise便于鏈?zhǔn)秸{(diào)用
超時(shí)處理:設(shè)置合理的請求超時(shí)時(shí)間
擴(kuò)展性:支持自定義配置和攔截器
5. 實(shí)際項(xiàng)目中的增強(qiáng)功能
1.自動重試機(jī)制:
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);
2.請求取消功能:
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 }; }
3.請求緩存:
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; }); }
根據(jù)項(xiàng)目需求選擇合適的封裝方式,小型項(xiàng)目可使用簡單封裝,大型項(xiàng)目建議使用成熟的庫如Axios。
到此這篇關(guān)于AJAX常見的幾種封裝方法的文章就介紹到這了,更多相關(guān)AJAX封裝方法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
實(shí)現(xiàn)類似facebook無刷新ajax更新
這篇文章主要介紹了實(shí)現(xiàn)類似facebook無刷新ajax更新,需要的朋友可以參考下2014-03-03PHP Ajax實(shí)現(xiàn)頁面無刷新發(fā)表評論
PHP Ajax實(shí)現(xiàn)頁面無刷新發(fā)表評論...2006-12-12