【HTML5】【AJAX的幾種封裝方法詳解】

【HTML5】【AJAX的幾種封裝方法詳解】

AJAX (Asynchronous JavaScript and XML) 封裝是為了簡化重復的異步請求代碼,提高開發效率和代碼復用性。下面我將介紹幾種常見的 AJAX 封裝方式。
在這里插入圖片描述

方法1. 基于原生 XMLHttpRequest 的封裝

XMLHttpRequest。其主要特點如下:

  1. 實現動態不刷新,通過異步?式,提升?戶體驗,優化了瀏覽器和服務器之間的傳輸。
  2. 把?部分原本由服務器負擔的?作轉移到客戶端,利?客戶端閑置的資源進?處理,減輕服務器和帶寬的負擔,節約空間和成本。
  3. ?刷新更新??,?戶不?再像以前?樣在服務器處理數據時,只能在死板的?屏前焦急的等待。AJAX使?XMLHttpRequest對象發送請求并得到服務器響應,在不需要重新載?整個??的情況下,就可以通過DOM及時將更新的內容顯示在??上。
/*** 基于原生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);}
});

方法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請求的查詢參數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));

方法 3. 基于 Axios 風格的封裝

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));

4. 封裝要點總結

統一接口:提供一致的調用方式,如get(), post()等方法

參數處理:

GET請求自動拼接查詢參數

POST請求自動處理Content-Type

攔截器機制:支持請求/響應攔截

錯誤處理:統一錯誤處理邏輯

Promise支持:返回Promise便于鏈式調用

超時處理:設置合理的請求超時時間

擴展性:支持自定義配置和攔截器

5. 實際項目中的增強功能

1.自動重試機制:

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;});
}

根據項目需求選擇合適的封裝方式,小型項目可使用簡單封裝,大型項目建議使用成熟的庫如Axios。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/81721.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/81721.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/81721.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C++ - 網絡編程之初始連接(Winsock2 概述、初始連接案例、初始連接案例解讀)

一、Winsock2 概述 Winsock2&#xff08;Windows Sockets 2&#xff09;是微軟提供的 Windows 平臺網絡編程庫 二、初始連接案例 1、Server #include <winsock2.h> #include <ws2tcpip.h> #include <iostream>#pragma comment(lib, "ws2_32.lib&quo…

Spring Cloud Gateway深度解析:原理、架構與生產實踐

文章目錄 前言一、概述二、核心架構設計及設計原理2.1 分層架構模型網絡層&#xff08;I/O模型&#xff09;核心處理層 2.2 核心組件協作流程路由定位階段過濾器執行階段 2.3 響應式編程模型實現Reactor上下文傳遞背壓處理機制 2.4 動態路由設計原理2.5 異常處理體系2.6 關鍵路…

游戲開發實戰(一):Python復刻「崩壞星穹鐵道」嗷嗚嗷嗚事務所---源碼級解析該小游戲背后的算法與設計模式【純原創】

文章目錄 奇美拉項目游戲規則奇美拉(Chimeras)檔案領隊成員 結果展示&#xff1a; 奇美拉項目 由于項目工程較大&#xff0c;并且我打算把我的思考過程和實現過程中踩過的坑都分享一下&#xff0c;因此會分3-4篇博文詳細講解本項目。本文首先介紹下游戲規則并給出奇美拉檔案。…

說一下響應狀態碼有哪些?

HTTP響應狀態碼分類(RFC 7231標準) 1. 1xx(信息類) 臨時響應,表示請求已被接收,需要繼續處理 100 Continue:客戶端應繼續發送請求體 101 Switching Protocols:服務器同意升級協議(如WebSocket) 102 Processing(WebDAV):服務器正在處理但未完成 2. 2xx(成功類)…

Linux多進程 寫時拷貝 物理地址和邏輯地址

如果不采用寫時拷貝技術 直接fork子進程 會發生什么&#xff1f; 如上圖所示 橙色為父進程所占內存空間 綠色為子進程所占內存空間。 如果子進程只是需要做出一點點和父進程不一樣的 其余和父進程均為相同 第一 就會出現復制開銷比較大&#xff1b;第二占用內存空間 所以 …

【TTS回顧】Bert-VITS2深度解析:融合BERT的多語言語音合成模型

一、基本介紹 Bert-VITS2是基于VITS(Variational Inference with adversarial learning for end-to-end Text-to-Speech)的改進版本,通過整合BERT語義編碼能力,顯著提升了語音合成的自然度和表現力。項目地址:https://github.com/fishaudio/Bert-VITS2 語種自然度相似度流…

win11下docker 的使用方案

Windows 11 Docker 使用方式對比 特性Docker Desktop (使用 WSL 2 后端)直接在 WSL 2 中安裝 Docker Engine安裝與易用性極簡&#xff0c;一鍵安裝&#xff0c;提供直觀的 GUI 界面 管理容器、鏡像、卷等相對復雜&#xff0c;需手動在 Linux 環境中安裝 Docker Daemon 并配置G…

配合本專欄前端文章對應的后端文章——從模擬到展示:一步步搭建傳感器數據交互系統

對應文章&#xff1a;進一步完善前端框架搭建及vue-konva依賴的使用&#xff08;Vscode&#xff09;-CSDN博客 目錄 一、后端開發 1.模擬傳感器數據 2.前端頁面呈現數據后端互通 2.1更新模擬傳感器數據程序&#xff08;多次請求&#xff09; 2.2&#x1f9e9; 功能目標 …

牛客網NC209794:使徒襲來

牛客網NC209794:使徒襲來 題目背景 問題分析 數學建模 設三位駕駛員的戰斗力分別為 a, b, c已知條件&#xff1a;a b c n (n為輸入的正整數)目標&#xff1a;求 a b c 的最小值 解題思路 根據算術-幾何平均值不等式(AM-GM不等式)&#xff0c;對于任意正實數a, b, c&a…

動態規劃之爬樓梯模型

文章目錄 爬樓梯模型LeetCode 746. 使用最小花費爬樓梯思路Golang 代碼 LeetCode 377. 組合總和 Ⅳ思路Golang 代碼 LeetCode 2466. 統計構造好字符串的方案數思路Golang 代碼 LeetCode 2266. 統計打字方案數思路Golang 代碼 爬樓梯模型 爬樓梯模型是動態規劃當中的一個經典模型…

【每天一個知識點】湖倉一體(Data Lakehouse)

“湖倉一體”&#xff08;Data Lakehouse&#xff09;是一種融合了數據湖&#xff08;Data Lake&#xff09;與數據倉庫&#xff08;Data Warehouse&#xff09;優勢的新型數據架構。它既繼承了數據湖對多類型數據的靈活存儲能力&#xff0c;也具備數據倉庫對結構化數據的高效查…

Linux | mdadm 創建軟 RAID

注&#xff1a;本文為 “Linux mdadm RAID” 相關文章合輯。 略作重排&#xff0c;未整理去重。 如有內容異常&#xff0c;請看原文。 Linux 下用 mdadm 創建軟 RAID 以及避坑 喵??&#xfecc;?? Oct 31, 2023 前言 linux 下組軟 raid 用 mdadm 命令&#xff0c;multi…

Unity自定義shader打包SpriteAtlas圖集問題

Unity打包圖集還是有一些坑的&#xff0c;至于圖集SpriteAtlas是什么請參考我之前寫的文章&#xff1a;【Sprite Atlas】Unity新圖集系統SpriteAtlas超詳細使用教程_spriteatlas 使用-CSDN博客 問題&#xff1a; 今天碰到的問題是&#xff0c;shader繪制的時候&#xff0c;因…

如何用 OceanBase 的 LOAD DATA 旁路導入進行大表遷移

前言 在日常工作中&#xff0c;我們時常會遇到需要將某個大數據量的單表進行遷移的情況。在MySQL中&#xff0c;針對這樣的大表&#xff0c;我們通常會選擇先將原表導出為csv格式&#xff0c;然后利用LOAD DATA語法來導入csv文件&#xff0c;這種方法相較于mysqldump在效率上有…

VR 互動實訓的顯著優勢?

&#xff08;一&#xff09;沉浸式學習&#xff0c;提升培訓效果? 在 VR 互動實訓中&#xff0c;員工不再是被動的知識接受者&#xff0c;而是主動的參與者。以銷售培訓為例&#xff0c;員工戴上 VR 設備&#xff0c;就能置身于逼真的銷售場景中&#xff0c;與虛擬客戶進行面對…

OpenCV 第6課 圖像處理之幾何變換(重映射)

1. 概述 簡單來說,重映射就是把一副圖像內的像素點按照規則映射到到另外一幅圖像內的對應位置上去,形成一張新的圖像。 因為原圖像與目標圖像的像素坐標不是一一對應的。一般情況下,我們通過重映射來表達每個像素的位置(x,y),像這樣: g(x,y)=f(h(x,y)) 在這里g()是目標圖…

Java虛擬機 - 程序計數器和虛擬機棧

運行時數據結構 Java運行時數據區程序計數器為什么需要程序計數器執行流程虛擬機棧虛擬機棧作用虛擬機棧核心結構運行機制 Java運行時數據區 首先介紹Java運行時數據之前&#xff0c;我們要了解&#xff0c;對于計算機來說&#xff0c;內存是非常重要的資源&#xff0c;因為內…

MySQL數據庫——支持遠程IP訪問的設置方法總結

【系列專欄】&#xff1a;博主結合工作實踐輸出的&#xff0c;解決實際問題的專欄&#xff0c;朋友們看過來&#xff01; 《項目案例分享》 《極客DIY開源分享》 《嵌入式通用開發實戰》 《C語言開發基礎總結》 《從0到1學習嵌入式Linux開發》 《QT開發實戰》 《Android開發實…

CSS- 4.6 radiu、shadow、animation動畫

本系列可作為前端學習系列的筆記&#xff0c;代碼的運行環境是在HBuilder中&#xff0c;小編會將代碼復制下來&#xff0c;大家復制下來就可以練習了&#xff0c;方便大家學習。 HTML系列文章 已經收錄在前端專欄&#xff0c;有需要的寶寶們可以點擊前端專欄查看&#xff01; 點…

排序算法之基礎排序:冒泡,選擇,插入排序詳解

排序算法之基礎排序&#xff1a;冒泡、選擇、插入排序詳解 前言一、冒泡排序&#xff08;Bubble Sort&#xff09;1.1 算法原理1.2 代碼實現&#xff08;Python&#xff09;1.3 性能分析 二、選擇排序&#xff08;Selection Sort&#xff09;2.1 算法原理2.2 代碼實現&#xff…