当前位置: 首页 > news >正文

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.dtcms.com/a/354667.html

相关文章:

  • React Hook+Ts+Antd+SpringBoot实现分片上传(前端)
  • openEuler常用操作指令
  • Java开发 - 缓存
  • 我店生活平台是不是 “圈钱平台”?揭开消费补贴新模式的面纱
  • 从零开始的云计算生活——第五十三天,发愤图强,kubernetes模块之Prometheus和发布
  • DistributedLock 实现.Net分布式锁
  • Kafka02-集群选主
  • BeyondMimic——通过引导式扩散实现动作捕捉:基于Diffuse-CLoC构建扩散框架,可模仿动作、导航避障(含UniTracker的详解)
  • InstructGPT:使用人类反馈训练语言模型以遵循指令
  • ARM相关的基础概念和寄存器
  • Shell编程知识整理
  • 从 WPF 到 Avalonia 的迁移系列实战篇2:路由事件的异同点与迁移技巧
  • Linux下OpenRadioss源码编译安装及使用
  • Shell 字符串操作与运算符
  • 利用ChatGPT打造行业LLM大模型应用
  • 外部请求至k8s集群内部对应节点全流程介绍
  • 使用docker搭建嵌入式Linux开发环境
  • HTML5七夕节网站源码
  • Java:TCP/UDP网络编程
  • DevOps篇之利用Jenkins实现多K8S集群的版本发布
  • Docker-compose常用命令
  • Helm 在 K8s 中的常见应用场景
  • 【K8s】整体认识K8s之K8s的控制器
  • Node.js + MongoDB 搭建 RESTful API 实战教程
  • 从入门到入土之——奇异值分解(SVD)
  • 重塑可观测性成本:解析Coralogix的智能成本优化之道
  • 深入浅出:贴片式eMMC存储与国产芯(君正/瑞芯微)的协同设计指南
  • GitHub 宕机自救指南:确保开发工作不间断
  • 学习做动画6.瞄准偏移
  • 5.2 I/O软件