JavaWeb 30 天入門:第二十一天 ——AJAX 異步交互技術

????????在前二十天的學習中,我們掌握了 JavaWeb 開發的核心技術,包括 Servlet、JSP、會話管理、過濾器、監聽器、文件操作、數據庫交互、連接池、分頁與排序等。今天我們將學習一項徹底改變 Web 應用交互方式的技術 ——AJAX(Asynchronous JavaScript and XML)

傳統的 Web 應用中,每次數據交互都需要刷新整個頁面,用戶體驗較差。AJAX 通過在后臺與服務器進行異步數據交換,使網頁可以在不重新加載整個頁面的情況下,實現部分內容的更新。這項技術是現代 Web 應用(如 Gmail、Facebook、微博等)實現流暢用戶體驗的基礎。

AJAX 概述

什么是 AJAX

AJAX?是一種在無需重新加載整個網頁的情況下,能夠更新部分網頁的技術。

  • Asynchronous(異步):指與服務器通信時,瀏覽器不需要暫停等待服務器響應,可以繼續執行其他操作
  • JavaScript:核心編程語言,用于發送請求、處理響應和更新頁面
  • And:連接詞
  • XML:早期主要用于數據交換的格式,現在 JSON 更常用

AJAX 的核心是XMLHttpRequest 對象(XHR),它允許瀏覽器與服務器進行異步通信。

AJAX 的工作原理

AJAX 的工作流程如下:

  1. 用戶在網頁上執行某個操作(如點擊按鈕、輸入文本)
  2. JavaScript 捕獲該事件,創建 XMLHttpRequest 對象
  3. XMLHttpRequest 對象向服務器發送異步請求
  4. 服務器處理請求,返回數據(通常是 JSON 或 XML 格式)
  5. JavaScript 接收服務器返回的數據
  6. JavaScript 更新網頁的部分內容,而無需重新加載整個頁面

AJAX 的優勢

  1. 提升用戶體驗:無需刷新整個頁面,減少等待時間和視覺干擾
  2. 減少數據傳輸:只傳輸需要更新的數據,節省帶寬
  3. 提高交互性:可以實現實時驗證、自動完成等高級交互功能
  4. 減輕服務器負擔:部分數據處理可以在客戶端完成
  5. 支持離線功能:結合現代 API 可以實現數據本地存儲和離線操作

AJAX 的應用場景

  1. 表單實時驗證(如用戶名是否已存在)
  2. 動態加載數據(如下拉列表聯動、滾動加載更多)
  3. 實時搜索建議(輸入時自動提示匹配結果)
  4. 無刷新分頁和排序
  5. 實時數據展示(如股票行情、在線聊天)
  6. 文件上傳進度顯示

XMLHttpRequest 對象

XMLHttpRequest(XHR)是 AJAX 的核心對象,用于在后臺與服務器交換數據。

創建 XHR 對象

不同瀏覽器創建 XHR 對象的方式略有差異,標準寫法如下:

// 創建XMLHttpRequest對象
function createXHR() {var xhr;if (window.XMLHttpRequest) {// 現代瀏覽器(IE7+、Firefox、Chrome、Safari等)xhr = new XMLHttpRequest();} else {// 兼容IE6及以下版本xhr = new ActiveXObject("Microsoft.XMLHTTP");}return xhr;
}

XHR 對象的常用屬性

屬性描述
readyState請求的狀態碼:0 - 未初始化,1 - 服務器連接已建立,2 - 請求已接收,3 - 請求處理中,4 - 請求已完成且響應已就緒
status服務器返回的 HTTP 狀態碼:200 - 成功,404 - 未找到,500 - 服務器內部錯誤等
statusText服務器返回的狀態文本(如 "OK"、"Not Found")
responseText服務器返回的文本數據
responseXML服務器返回的 XML 數據(可作為 DOM 對象處理)
onreadystatechange每當readyState改變時觸發的事件處理函數

XHR 對象的常用方法

方法描述
open(method, url, async)初始化請求:
- method:請求方法(GET、POST 等)
- url:請求地址
- async:是否異步(true - 異步,false - 同步)
send(data)發送請求:
- data:POST 請求時的參數數據
setRequestHeader(header, value)設置請求頭信息(需在open()之后、send()之前調用)
abort()取消當前請求
getResponseHeader(header)獲取指定響應頭的值
getAllResponseHeaders()獲取所有響應頭信息

AJAX 的基本使用步驟

使用 AJAX 與服務器交互的基本步驟:

  1. 創建 XMLHttpRequest 對象
  2. 注冊onreadystatechange事件處理函數
  3. 使用open()方法初始化請求
  4. (可選)設置請求頭信息
  5. 使用send()方法發送請求
  6. 在事件處理函數中處理服務器響應

1. GET 請求示例

GET 請求通常用于從服務器獲取數據,參數通過 URL 的查詢字符串傳遞:

// 發送GET請求
function sendGetRequest() {// 1. 創建XHR對象var xhr = createXHR();// 2. 注冊事件處理函數xhr.onreadystatechange = function() {// 當請求完成且響應就緒if (xhr.readyState === 4) {// 當HTTP狀態碼為200(成功)if (xhr.status === 200) {// 處理響應數據var response = xhr.responseText;console.log("服務器響應:", response);document.getElementById("result").innerHTML = response;} else {// 處理錯誤console.error("請求失敗,狀態碼:", xhr.status);document.getElementById("result").innerHTML = "請求失敗:" + xhr.statusText;}}};// 3. 初始化請求(帶參數)var username = document.getElementById("username").value;// 對參數進行編碼,防止特殊字符問題var url = "GetDataServlet?username=" + encodeURIComponent(username) + "&t=" + new Date().getTime();xhr.open("GET", url, true);// 4. 發送請求(GET請求參數在URL中,send()方法參數為null)xhr.send(null);
}

注意

  • GET 請求的參數會顯示在 URL 中,安全性較低
  • GET 請求有長度限制(不同瀏覽器限制不同,通常 2KB-8KB)
  • 添加時間戳(t=new Date().getTime())是為了避免瀏覽器緩存

2. POST 請求示例

POST 請求通常用于向服務器提交數據,參數在請求體中傳遞:

// 發送POST請求
function sendPostRequest() {// 1. 創建XHR對象var xhr = createXHR();// 2. 注冊事件處理函數xhr.onreadystatechange = function() {if (xhr.readyState === 4) {if (xhr.status === 200) {var response = xhr.responseText;console.log("服務器響應:", response);document.getElementById("result").innerHTML = response;} else {console.error("請求失敗,狀態碼:", xhr.status);document.getElementById("result").innerHTML = "請求失敗:" + xhr.statusText;}}};// 3. 初始化請求var url = "PostDataServlet";xhr.open("POST", url, true);// 4. 設置請求頭(POST請求需要設置Content-Type)xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");// 5. 準備請求參數var username = document.getElementById("username").value;var email = document.getElementById("email").value;// 對參數進行編碼var data = "username=" + encodeURIComponent(username) + "&email=" + encodeURIComponent(email);// 6. 發送請求xhr.send(data);
}

POST vs GET

特性GETPOST
參數位置URL 查詢字符串請求體
長度限制無(由服務器配置決定)
緩存可被緩存通常不被緩存
安全性低(參數可見)較高(參數在請求體)
用途獲取數據提交數據
冪等性是(多次請求結果相同)否(可能產生副作用)

處理 JSON 數據

現代 Web 應用中,JSON(JavaScript Object Notation)已成為 AJAX 數據交換的首選格式,它比 XML 更輕量、更易解析。

1. 服務器返回 JSON 數據

在 Servlet 中返回 JSON 數據:

import com.fasterxml.jackson.databind.ObjectMapper;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;@WebServlet("/JsonDataServlet")
public class JsonDataServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 設置響應內容類型為JSONresponse.setContentType("application/json;charset=UTF-8");// 創建返回數據對象Map<String, Object> result = new HashMap<>();try {// 獲取請求參數String username = request.getParameter("username");// 模擬數據庫查詢boolean exists = "admin".equals(username);// 構建響應數據result.put("success", true);result.put("message", exists ? "用戶名已存在" : "用戶名可用");result.put("exists", exists);} catch (Exception e) {result.put("success", false);result.put("message", "服務器錯誤:" + e.getMessage());}// 使用Jackson庫將Java對象轉換為JSON字符串ObjectMapper mapper = new ObjectMapper();String json = mapper.writeValueAsString(result);// 發送JSON響應response.getWriter().write(json);}
}

添加 Jackson 依賴(Maven):

<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.13.3</version>
</dependency>

2. 客戶端解析 JSON 數據

客戶端使用JSON.parse()方法解析 JSON 字符串:

// 發送請求并處理JSON響應
function checkUsername() {var xhr = createXHR();xhr.onreadystatechange = function() {if (xhr.readyState === 4 && xhr.status === 200) {// 解析JSON響應try {var result = JSON.parse(xhr.responseText);var messageElement = document.getElementById("message");if (result.success) {// 處理成功響應messageElement.textContent = result.message;messageElement.style.color = result.exists ? "red" : "green";} else {// 處理錯誤響應messageElement.textContent = "錯誤:" + result.message;messageElement.style.color = "red";}} catch (e) {console.error("JSON解析錯誤:", e);document.getElementById("message").textContent = "數據格式錯誤";}}};var username = document.getElementById("username").value;var url = "JsonDataServlet?username=" + encodeURIComponent(username) + "&t=" + new Date().getTime();xhr.open("GET", url, true);xhr.send(null);
}

3. 用戶名實時驗證示例

結合上述代碼,實現一個用戶名實時驗證功能:

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head><title>用戶名實時驗證</title><style>.container { width: 500px; margin: 100px auto; }.form-group { margin: 20px 0; }label { display: inline-block; width: 100px; }input { padding: 8px; width: 250px; }#message { margin-left: 105px; height: 20px; }</style>
</head>
<body><div class="container"><h2>注冊</h2><div class="form-group"><label for="username">用戶名:</label><input type="text" id="username" onblur="checkUsername()" onkeyup="debounceCheckUsername()"></div><div id="message"></div><div class="form-group"><label for="password">密碼:</label><input type="password" id="password"></div><div class="form-group"><input type="button" value="注冊" onclick="register()"></div></div><script>// 創建XHR對象的函數function createXHR() {var xhr;if (window.XMLHttpRequest) {xhr = new XMLHttpRequest();} else {xhr = new ActiveXObject("Microsoft.XMLHTTP");}return xhr;}// 防抖函數(避免輸入時頻繁請求)var timeout = null;function debounceCheckUsername() {clearTimeout(timeout);// 延遲500毫秒執行,避免輸入過程中頻繁請求timeout = setTimeout(checkUsername, 500);}// 檢查用戶名函數(前面已定義)function checkUsername() {// ... 實現代碼同上 ...}// 注冊函數function register() {// ... 實現注冊邏輯 ...}</script>
</body>
</html>

AJAX 與表單提交

使用 AJAX 提交表單可以避免頁面刷新,同時提供更靈活的錯誤處理和用戶反饋。

1. 基本表單提交

// 使用AJAX提交表單
function submitForm() {// 獲取表單數據var username = document.getElementById("username").value;var password = document.getElementById("password").value;var email = document.getElementById("email").value;// 簡單驗證if (!username || !password || !email) {alert("請填寫完整信息");return;}// 創建XHR對象var xhr = createXHR();// 處理響應xhr.onreadystatechange = function() {if (xhr.readyState === 4) {if (xhr.status === 200) {try {var result = JSON.parse(xhr.responseText);if (result.success) {// 注冊成功alert("注冊成功!");// 可以跳轉到登錄頁// window.location.href = "login.jsp";} else {// 注冊失敗alert("注冊失敗:" + result.message);}} catch (e) {alert("服務器響應格式錯誤");}} else {alert("請求失敗,狀態碼:" + xhr.status);}}};// 發送POST請求xhr.open("POST", "RegisterServlet", true);xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");// 構建表單數據var data = "username=" + encodeURIComponent(username) +"&password=" + encodeURIComponent(password) +"&email=" + encodeURIComponent(email);xhr.send(data);
}

2. 處理文件上傳

AJAX 也可以處理文件上傳,需要使用FormData對象:

// 使用AJAX上傳文件
function uploadFile() {// 獲取文件輸入元素var fileInput = document.getElementById("file");var file = fileInput.files[0];// 檢查文件是否選擇if (!file) {alert("請選擇要上傳的文件");return;}// 檢查文件類型var allowedTypes = ["image/jpeg", "image/png", "image/gif"];if (!allowedTypes.includes(file.type)) {alert("只允許上傳JPG、PNG、GIF格式的圖片");return;}// 檢查文件大小(限制5MB)if (file.size > 5 * 1024 * 1024) {alert("文件大小不能超過5MB");return;}// 創建FormData對象var formData = new FormData();formData.append("file", file);formData.append("description", document.getElementById("description").value);// 創建XHR對象var xhr = createXHR();// 處理上傳進度xhr.upload.onprogress = function(event) {if (event.lengthComputable) {var percent = (event.loaded / event.total) * 100;document.getElementById("progressBar").style.width = percent + "%";document.getElementById("progressText").textContent = Math.round(percent) + "%";}};// 處理響應xhr.onreadystatechange = function() {if (xhr.readyState === 4) {if (xhr.status === 200) {var result = JSON.parse(xhr.responseText);if (result.success) {alert("上傳成功!");document.getElementById("result").innerHTML = "文件路徑:" + result.filePath + "<br>" +"預覽:<img src='" + result.filePath + "' style='max-width: 300px;'>";} else {alert("上傳失敗:" + result.message);}} else {alert("上傳失敗,狀態碼:" + xhr.status);}}};// 發送請求xhr.open("POST", "FileUploadServlet", true);// 上傳文件時不要設置Content-Type,瀏覽器會自動處理xhr.send(formData);
}

對應的 JSP 頁面:

<div class="form-group"><label for="file">選擇文件:</label><input type="file" id="file" accept="image/*">
</div>
<div class="form-group"><label for="description">描述:</label><input type="text" id="description" placeholder="請輸入文件描述">
</div>
<div class="progress" style="width: 360px; height: 20px; border: 1px solid #ccc; margin-left: 105px;"><div id="progressBar" style="width: 0%; height: 100%; background-color: #4CAF50;"></div>
</div>
<div id="progressText" style="margin-left: 105px; margin-top: 5px;">0%</div>
<div class="form-group"><input type="button" value="上傳" onclick="uploadFile()">
</div>
<div id="result" style="margin-left: 105px; margin-top: 10px;"></div>

AJAX 異步分頁案例

結合之前的分頁技術,使用 AJAX 實現無刷新分頁:

1. 分頁 Servlet

@WebServlet("/AjaxUserPageServlet")
public class AjaxUserPageServlet extends HttpServlet {private UserDAO userDAO = new UserDAO();@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {response.setContentType("application/json;charset=UTF-8");// 獲取分頁參數int currentPage = 1;int pageSize = 10;try {currentPage = Integer.parseInt(request.getParameter("currentPage"));pageSize = Integer.parseInt(request.getParameter("pageSize"));} catch (NumberFormatException e) {// 使用默認值}// 獲取查詢條件String username = request.getParameter("username");// 獲取排序參數String sortField = request.getParameter("sortField");String sortOrder = request.getParameter("sortOrder");// 查詢分頁數據PageBean<User> pageBean = new PageBean<>(pageSize, currentPage);pageBean.setSortField(sortField);pageBean.setSortOrder(sortOrder);if (username != null && !username.trim().isEmpty()) {pageBean = userDAO.getUsersByConditionSortAndPage(username.trim(), pageBean);} else {pageBean = userDAO.getUsersByConditionSortAndPage(null, pageBean);}// 轉換為JSON并響應ObjectMapper mapper = new ObjectMapper();// 處理日期格式mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));String json = mapper.writeValueAsString(pageBean);response.getWriter().write(json);}
}

2. 客戶端分頁實現

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head><title>AJAX分頁示例</title><style>/* 樣式省略,參考之前的分頁頁面 */</style>
</head>
<body><div class="container"><h2>用戶列表(AJAX分頁)</h2><!-- 搜索欄 --><div class="search-bar"><input type="text" id="username" placeholder="請輸入用戶名搜索"><input type="button" value="搜索" onclick="loadPage(1)"><select id="pageSize" onchange="loadPage(1)"><option value="5">5條/頁</option><option value="10" selected>10條/頁</option><option value="20">20條/頁</option></select></div><!-- 數據表格 --><table><thead><tr><th>ID</th><th><a href="javascript:sortBy('username')">用戶名</a></th><th><a href="javascript:sortBy('email')">郵箱</a></th><th><a href="javascript:sortBy('createTime')">注冊時間</a></th><th><a href="javascript:sortBy('status')">狀態</a></th></tr></thead><tbody id="userTableBody"><!-- 數據將通過AJAX動態加載 --><tr><td colspan="5" style="text-align: center;">加載中...</td></tr></tbody></table><!-- 分頁導航 --><div id="pagination" class="page-nav"><!-- 分頁導航將通過AJAX動態生成 --></div><!-- 加載狀態提示 --><div id="loading" style="display: none; text-align: center; padding: 20px;">加載中...</div></div><script>// 當前頁碼和分頁參數var currentPage = 1;var pageSize = 10;var sortField = "createTime";var sortOrder = "DESC";// 頁面加載完成后加載第一頁數據window.onload = function() {loadPage(1);};// 加載指定頁數據function loadPage(pageNum) {// 顯示加載狀態document.getElementById("loading").style.display = "block";// 更新當前頁碼currentPage = pageNum;// 獲取查詢條件var username = document.getElementById("username").value.trim();pageSize = document.getElementById("pageSize").value;// 創建XHR對象var xhr = createXHR();// 處理響應xhr.onreadystatechange = function() {if (xhr.readyState === 4) {// 隱藏加載狀態document.getElementById("loading").style.display = "none";if (xhr.status === 200) {try {var pageBean = JSON.parse(xhr.responseText);// 更新表格數據updateTable(pageBean.dataList);// 更新分頁導航updatePagination(pageBean);} catch (e) {console.error("解析JSON失敗:", e);document.getElementById("userTableBody").innerHTML = "<tr><td colspan='5' style='text-align: center; color: red;'>數據格式錯誤</td></tr>";}} else {document.getElementById("userTableBody").innerHTML = "<tr><td colspan='5' style='text-align: center; color: red;'>加載失敗,狀態碼:" + xhr.status + "</td></tr>";}}};// 構建請求URLvar url = "AjaxUserPageServlet?" +"currentPage=" + pageNum +"&pageSize=" + pageSize +"&username=" + encodeURIComponent(username) +"&sortField=" + sortField +"&sortOrder=" + sortOrder +"&t=" + new Date().getTime();// 發送請求xhr.open("GET", url, true);xhr.send(null);}// 更新表格數據function updateTable(userList) {var tableBody = document.getElementById("userTableBody");if (userList.length === 0) {tableBody.innerHTML = "<tr><td colspan='5' style='text-align: center;'>暫無數據</td></tr>";return;}var html = "";for (var i = 0; i < userList.length; i++) {var user = userList[i];html += "<tr>";html += "<td>" + user.id + "</td>";html += "<td>" + user.username + "</td>";html += "<td>" + user.email + "</td>";html += "<td>" + user.createdTime + "</td>";html += "<td>" + (user.status === 1 ? "<span style='color: green;'>正常</span>" : "<span style='color: red;'>禁用</span>") + "</td>";html += "</tr>";}tableBody.innerHTML = html;}// 更新分頁導航function updatePagination(pageBean) {var pagination = document.getElementById("pagination");var html = "";// 首頁html += "<a href='javascript:loadPage(1)' " + (pageBean.currentPage === 1 ? "style='pointer-events: none; opacity: 0.5;'" : "") + ">首頁</a>";// 上一頁html += "<a href='javascript:loadPage(" + pageBean.prevPage + ")' " + (!pageBean.hasPrevPage ? "style='pointer-events: none; opacity: 0.5;'" : "") + ">上一頁</a>";// 頁碼var startPage = Math.max(1, pageBean.currentPage - 3);var endPage = Math.min(pageBean.totalPage, pageBean.currentPage + 3);// 調整頁碼范圍if (endPage - startPage < 6 && pageBean.totalPage > 6) {if (startPage === 1) {endPage = 7;} else if (endPage === pageBean.totalPage) {startPage = pageBean.totalPage - 6;}}for (var i = startPage; i <= endPage; i++) {if (i === pageBean.currentPage) {html += "<span class='active'>" + i + "</span>";} else {html += "<a href='javascript:loadPage(" + i + ")'>" + i + "</a>";}}// 下一頁html += "<a href='javascript:loadPage(" + pageBean.nextPage + ")' " + (!pageBean.hasNextPage ? "style='pointer-events: none; opacity: 0.5;'" : "") + ">下一頁</a>";// 末頁html += "<a href='javascript:loadPage(" + pageBean.totalPage + ")' " + (pageBean.currentPage === pageBean.totalPage ? "style='pointer-events: none; opacity: 0.5;'" : "") + ">末頁</a>";// 分頁信息html += "<span>共 " + pageBean.totalCount + " 條記錄,共 " + pageBean.totalPage + " 頁,當前第 " + pageBean.currentPage + " 頁</span>";pagination.innerHTML = html;}// 排序功能function sortBy(field) {if (sortField === field) {// 切換排序方向sortOrder = sortOrder === "ASC" ? "DESC" : "ASC";} else {// 新的排序字段,默認降序sortField = field;sortOrder = "DESC";}// 重新加載第一頁loadPage(1);}// 創建XHR對象的函數function createXHR() {var xhr;if (window.XMLHttpRequest) {xhr = new XMLHttpRequest();} else {xhr = new ActiveXObject("Microsoft.XMLHTTP");}return xhr;}</script>
</body>
</html>

AJAX 最佳實踐

1. 錯誤處理

完善的錯誤處理是 AJAX 應用的重要組成部分:

// 健壯的AJAX錯誤處理
function safeAjaxRequest(url, method, data, successCallback, errorCallback) {// 參數驗證if (!url || !method) {if (errorCallback) errorCallback(new Error("URL和請求方法不能為空"));return;}var xhr = createXHR();// 超時設置(5秒)xhr.timeout = 5000;xhr.onreadystatechange = function() {if (xhr.readyState === 4) {if (xhr.status === 200) {try {// 嘗試解析JSONvar response = JSON.parse(xhr.responseText);if (successCallback) successCallback(response);} catch (e) {if (errorCallback) {errorCallback(new Error("響應數據格式錯誤: " + e.message));} else {console.error("響應數據格式錯誤: ", e);}}} else {var errorMsg = "請求失敗,狀態碼: " + xhr.status;if (xhr.status === 404) errorMsg = "請求的資源不存在";if (xhr.status === 500) errorMsg = "服務器內部錯誤";if (errorCallback) {errorCallback(new Error(errorMsg));} else {console.error(errorMsg);}}}};// 網絡錯誤處理xhr.onerror = function() {var error = new Error("網絡錯誤,無法連接到服務器");if (errorCallback) errorCallback(error);else console.error(error.message);};// 超時處理xhr.ontimeout = function() {var error = new Error("請求超時,請稍后重試");if (errorCallback) errorCallback(error);else console.error(error.message);};// 發送請求xhr.open(method, url, true);if (method.toUpperCase() === "POST" && !(data instanceof FormData)) {xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");}xhr.send(data || null);// 返回xhr對象,允許調用abort()return xhr;
}

2. 性能優化

  1. 請求合并:將多個小請求合并為一個大請求,減少 HTTP 請求次數
  2. 請求防抖:對于頻繁觸發的事件(如輸入、滾動),延遲發送請求
  3. 緩存響應:對不常變化的數據進行本地緩存,減少重復請求
  4. 壓縮數據:使用 gzip 壓縮服務器響應,減少傳輸數據量
  5. 使用 HTTP/2:支持多路復用,提高并發請求效率
  6. 預加載:在空閑時預加載可能需要的數據

3. 安全性考慮

  1. 防止 XSS 攻擊

    • 服務器對輸出進行 HTML 轉義
    • 客戶端使用textContent而非innerHTML插入不可信內容
  2. 防止 CSRF 攻擊

    • 使用 CSRF 令牌驗證請求來源
    • 檢查 Referer 請求頭
  3. 數據驗證

    • 客戶端驗證僅作為輔助,必須在服務器端進行嚴格驗證
    • 對所有用戶輸入進行過濾和轉義
  4. 限制請求頻率

    • 服務器端實現限流機制,防止惡意請求
    • 客戶端添加請求間隔限制

4. 用戶體驗優化

  1. 加載狀態反饋

    • 顯示加載動畫或進度條
    • 提供取消請求的選項
  2. 錯誤提示友好

    • 使用用戶易懂的語言描述錯誤
    • 提供解決問題的建議
  3. 離線支持

    • 使用 Service Worker 緩存靜態資源
    • 實現離線操作和數據同步
  4. 進度指示

    • 對于耗時操作(如下載、上傳),顯示進度信息
    • 預估完成時間

現代 AJAX 替代方案

雖然原生 XMLHttpRequest 功能強大,但使用起來比較繁瑣。現代前端開發中,有更便捷的替代方案:

1. Fetch API

Fetch API 是現代瀏覽器提供的用于替代 XMLHttpRequest 的 API,基于 Promise,語法更簡潔:

// 使用Fetch API發送請求
fetch('JsonDataServlet?username=' + encodeURIComponent(username)).then(response => {if (!response.ok) {throw new Error('HTTP error, status = ' + response.status);}return response.json();}).then(data => {console.log('成功:', data);// 處理數據}).catch(error => {console.error('錯誤:', error);});

2. Axios

Axios 是一個流行的第三方 AJAX 庫,支持 Promise API,提供了更多功能:

// 使用Axios發送請求
axios.get('JsonDataServlet', {params: {username: username}
})
.then(response => {console.log('成功:', response.data);// 處理數據
})
.catch(error => {console.error('錯誤:', error);
});

在 JavaWeb 項目中使用 Axios,只需引入 CDN:

<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>

總結與實踐

知識點回顧

  1. AJAX 基礎

    • AJAX 允許在不刷新頁面的情況下與服務器交換數據
    • 核心是 XMLHttpRequest 對象,負責異步通信
    • 支持 GET 和 POST 等 HTTP 方法
  2. 數據交互

    • 服務器通常返回 JSON 格式數據
    • 客戶端使用 JSON.parse () 解析響應
    • 可以提交表單數據和上傳文件
  3. 高級應用

    • 實時驗證:如用戶名唯一性檢查
    • 異步分頁:無刷新加載分頁數據
    • 文件上傳:帶進度顯示的文件上傳
  4. 最佳實踐

    • 完善的錯誤處理和超時控制
    • 性能優化:請求合并、防抖、緩存
    • 安全性考慮:防止 XSS、CSRF 攻擊
    • 良好的用戶體驗:加載狀態、友好提示

實踐任務

  1. 實時聊天系統

    • 使用 AJAX 實現簡單的實時聊天功能
    • 定期輪詢服務器獲取新消息
    • 支持發送消息和顯示消息歷史
  2. 動態數據儀表盤

    • 實現數據的實時刷新
    • 添加圖表展示(使用 Chart.js)
    • 支持數據篩選和時間范圍選擇
  3. 無刷新購物車

    • 實現商品的添加、刪除、數量修改
    • 實時計算總價和優惠信息
    • 支持本地存儲購物車數據

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

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

相關文章

從枯燥C++到趣味音樂:我的Windows系統底層探索之旅

一段穿越計算機抽象層次的旅程&#xff0c;從高級語言到底層硬件&#xff0c;探索代碼如何創造美妙旋律第一章&#xff1a;初學C的枯燥與靈感閃現 當我第一次打開《C Primer Plus》這本厚重的教程時&#xff0c;面對那些晦澀的語法規則和抽象概念&#xff0c;確實感到有些枯燥乏…

taro+vue3+vite項目 tailwind 踩坑記,附修復后的模板源碼地址

tailwind 踩坑記 這&#xff0c;是taro官網地址&#xff1a;taro引入tailwind的教程 我完全按照上面的步驟來&#xff0c;結果根本無效&#xff08;文檔太過時了&#xff09; 我后來又按照 weapp-tailwindcss 的官方文檔做了一番修正&#xff1a; weapp-tailwindcss Taro (所…

LCEDA電氣規則

MARK點普通問題 鋪銅太靠近MARK點放置一個禁止區域&#xff0c;圓形編輯封裝

無人機Remote ID:天空中的數字車牌與未來空域管理

一架沒有牌照的汽車上路會被交管部門處罰,那么一架沒有“數字車牌”的無人機升空呢?隨著無人機Remote ID技術的推廣,未來天空中的每架無人機都將擁有自己的身份標識。 近年來,無人機呈爆炸式增長,從航拍攝影到物流配送,從農業植保到應急救援,應用場景不斷拓展。但隨著無…

自下而上的樹形dp

最大獨立集 1.藍橋舞會 link:1.藍橋舞會 - 藍橋云課 分析&#xff1a; code #include <bits/stdc.h> using namespace std; using ll long long; const ll MAXN 1e5 7; ll hpy[MAXN], fa[MAXN], dp[MAXN][2]; vector<ll> sons[MAXN];void dfs(ll u, ll fa) {…

Docker 詳解+示例

介 紹Docker 是一個開源的容器化平臺&#xff0c;它的核心目標是解決 “軟件在不同環境下運行不一致” 的問題&#xff0c;實現 “一次構建&#xff0c;到處運行” 。它基于 Linux 內核的底層技術&#xff0c;將應用程序及其依賴&#xff08;如庫文件、配置、運行環境等&#x…

洛谷 P2568 GCD-提高+/省選?

題目描述 給定正整數 nnn&#xff0c;求 1≤x,y≤n1\le x,y\le n1≤x,y≤n 且 gcd?(x,y)\gcd(x,y)gcd(x,y) 為素數的數對 (x,y)(x,y)(x,y) 有多少對。 輸入格式 只有一行一個整數&#xff0c;代表 nnn。 輸出格式 一行一個整數表示答案。 輸入輸出樣例 #1 輸入 #1 4輸…

軟件測試覆蓋率與質量保障專業經驗分享報告

測試覆蓋率的核心維度與評估標準 多維度定義與核心內涵 測試覆蓋率是衡量軟件測試完整性的關鍵指標體系,分為測試覆蓋率(黑盒視角:需求驗證程度)和代碼覆蓋率(白盒視角:代碼執行占比)兩大基礎類型。現代測試覆蓋體系已擴展至產品覆蓋、風險覆蓋、平臺/設備覆蓋、數據覆…

使用CCProxy搭建http/https代理服務器

下載 https://user.youngzsoft.com/ccproxy/update/ccproxysetup.exe 我們使用免費的即可&#xff0c;3個人。 啟動軟件 設置 更改局域網IP 我的電腦有多個IP&#xff0c;所以要手工指定。

ICCV 2025|TRACE:無需標注,用3D高斯直接學習物理參數,從視頻“預知”未來!

論文鏈接&#xff1a;https://arxiv.org/pdf/2507.01484導讀 準確預測道路智能體的運動對于自動駕駛的安全性至關重要。當前&#xff0c;現有的數據驅動方法直接預測未來軌跡&#xff0c;缺乏對駕駛行為的充分考慮&#xff0c;限制了可解釋性和可靠性。為此&#xff0c;本文引入…

TypeScript:symbol類型

symbol是TypeScript和JavaScript中的一種基本數據類型&#xff0c;表示唯一的、不可變的標識符。作為專業的前端工程師&#xff0c;理解symbol的特性對于構建安全可靠的代碼至關重要。1. symbol的核心特性唯一性&#xff1a;每個symbol值都是唯一的&#xff0c;即使創建時使用相…

【深度學習新浪潮】顯著性檢測最新研究進展(2022-2025)

1. 弱監督與主動學習 ASTE-AL框架(TPAMI 2024):提出對抗性時空集成主動學習方法,通過點標記數據集(每張圖像僅需10個標注點)達到全監督模型98%-99%的性能。其核心模塊包括: FPGD-PA對抗攻擊:通過無額外計算成本的自由梯度下降攻擊定位不確定像素。 時空集成策略:減少模…

Intern-S1-mini模型結構

模型介紹 Intern-S1-mini基于一個8B密集語言模型&#xff08;Qwen3&#xff09;和一個0.3B視覺編碼器&#xff08;InternViT&#xff09;&#xff0c;Intern-S1-mini 在5萬億個標記的多模態數據上進行了進一步預訓練&#xff0c;其中包括超過2.5萬億個科學領域的標記。這使得該…

linux 100個問答(持續更新)

1.常用命令 2.rsync常用命令rsync 是?個強?的?件同步和復制?具&#xff0c;?于在本地和遠程系統之間同步?件和目錄。以下是?些常用的 rsync 命令和選項&#xff1a;1. 基本的 rsync rsync 命令格式&#xff1a; bashCopy code rsync [options] source destination● sou…

零基礎玩轉STM32:深入理解ARM Cortex-M內核與寄存器編程

1. 什么是 STM32 STM32 是 ST&#xff08;意法半導體&#xff0c;STMicroelectronics&#xff09;公司推出的 32 位微控制器。 其內核基于 ARM Cortex-M 系列&#xff08;如 M0、M3、M4、M7&#xff09;&#xff0c;性能強大、功耗低、外設豐富。憑借高性價比和完善的生態&…

CentOS 修改密碼

在 CentOS&#xff08;以及大多數 Linux 系統&#xff09;下&#xff0c;你可以用以下命令打印當前用戶&#xff1a; whoami或者&#xff1a; echo $USER方法1&#xff1a;直接用 passwd 命令 直接用 passwd 命令修改&#xff1a; # 修改當前用戶密碼 passwd# 修改指定用戶密碼…

.NetCore 接入 Nacos,實現配置中心和服務注冊

因歷史項目&#xff08;.Netcore3.1&#xff09;需要&#xff0c;需要使用Nacos作為配置中心和服務發現&#xff0c;本文作為記錄使用Nacos的筆記。 文章目錄一、相關資料二、Nacos后臺增加配置三、代碼接入1、在appsettings.json中加入配置2、Program調整3、Startup調整4、啟動…

自學嵌入式第三十天:Linux系統編程-線程的控制

一、線程控制&#xff1a;互斥和同步對于線程的共享資源的競爭的處理&#xff1b;進程也能用&#xff0c;對進程競爭的系統資源的分配&#xff1b;二、互斥1.互斥&#xff1a;在多線程中對臨界資源的排他性&#xff08;獨占&#xff09;訪問&#xff1b;2.互斥機制&#xff08;…

EtherNet/IP 轉 Modbus 協議網關(三格電子)

一、產品概述 1.1 產品用途 SG-EIP-MOD-210 網關可以實現將 Modbus 接口設備連接到 EtherNet/IP 網 絡中。用戶不需要了解具體的 Modbus 和 EtherNet/IP 協議即可實現將 Modbus 設 備掛載到 EtherNet/IP 接口的 PLC 上&#xff0c;并和 Modbus 設備進行數據交互。拓撲結 構如…

MVCC的作用是什么

問題MVCC的作用是什么我的回答MVCC&#xff0c;全稱是Multi-Version Concurrency Control&#xff0c;多版本并發控制。這是數據庫管理系統中一種常用的并發控制機制&#xff0c;主要用于提高數據庫的并發性能。簡單來說&#xff0c;MVCC的核心思想是&#xff0c;當有人讀取數據…