什么是AJAX

AJAX 是一种用于创建快速动态网页的技术",AJAX 是基于现有前端技术的组合(非新发明的技术),核心包含 4 个部分:

  • JavaScript:发起异步请求、处理服务器响应、操作 DOM 更新页面;
  • XML/JSON:作为前后端数据交换的格式(早期用 XML,现在主流用 JSON,更轻量、易解析);
  • XMLHttpRequest/fetch API:浏览器提供的 "通信接口",负责在后台与服务器交换数据;
  • DOM(文档对象模型):通过 JavaScript 操作 DOM,实现 "局部页面更新

| | 传统网页(无 AJAX) | AJAX 网页 |
| 数据请求方式 | 同步请求:发送请求后,页面 "卡住",等待服务器响应 | 异步请求:请求在后台执行,不阻塞页面操作(如点击、滚动) |
| 页面更新方式 | 必须重新加载整个页面(即使只更新一个列表) | 仅更新需要变化的局部区域(如评论列表、商品价格) |
| 用户体验 | 等待时间长、页面频繁刷新,体验卡顿 | 无刷新、响应快,体验流畅(如微博下拉加载新内容) |

资源消耗 重新加载整个页面,浪费带宽(重复加载 CSS/JS) 仅传输少量数据(如 JSON),节省带宽和服务器资源

AJAX 的核心工作流程

  1. 触发事件:用户操作(如点击 "加载更多" 按钮),触发 JavaScript 函数;

  2. 创建请求对象 :JavaScript 创建XMLHttpRequest或调用fetch API,准备与服务器通信;

  3. 配置并发送请求 :指定请求方式(GET/POST)、请求地址(如/api/goods),并发送请求(带参数,如 "页码 = 2");

  4. 服务器处理请求 :服务器接收请求,查询数据库,返回包含商品数据的 JSON(如{"list": [商品1, 商品2]});

  5. 前端处理响应:AJAX 接收 JSON 数据,通过 JavaScript 解析数据,生成 HTML 结构(如商品卡片);

  6. 局部更新 DOM:将生成的 HTML 插入到 "商品列表" 容器中,完成局部更新,用户看到新商品。

javascript 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AJAX加载更多示例</title>
    <style>
        .container {
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
        }
        .product-list {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
            gap: 20px;
            margin-bottom: 30px;
        }
        .product-card {
            border: 1px solid #ddd;
            padding: 15px;
            border-radius: 8px;
        }
        .product-card img {
            width: 100%;
            height: 150px;
            object-fit: cover;
        }
        .load-more-btn {
            display: block;
            margin: 0 auto;
            padding: 10px 20px;
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
        }
        .load-more-btn:disabled {
            background-color: #6c757d;
            cursor: not-allowed;
        }
        .loading {
            text-align: center;
            color: #666;
            padding: 20px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>商品列表</h1>
        <!-- 商品列表容器 -->
        <div class="product-list" id="productList"></div>
        
        <!-- 加载更多按钮 -->
        <button class="load-more-btn" id="loadMoreBtn">加载更多</button>
    </div>

    <script>
        // 当前页码,从1开始
        let currentPage = 1;
        // 每页显示数量
        const pageSize = 3;
        // 是否正在加载中
        let isLoading = false;

        // 1. 触发事件:给按钮绑定点击事件
        document.getElementById('loadMoreBtn').addEventListener('click', loadMoreProducts);

        // 页面加载时先加载第一页数据
        loadMoreProducts();

        // 加载更多商品的函数
        async function loadMoreProducts() {
            // 防止重复点击
            if (isLoading) return;
            isLoading = true;
            
            const loadMoreBtn = document.getElementById('loadMoreBtn');
            const productList = document.getElementById('productList');
            
            // 显示加载状态
            loadMoreBtn.disabled = true;
            loadMoreBtn.textContent = '加载中...';
            productList.insertAdjacentHTML('beforeend', '<div class="loading">正在加载...</div>');

            try {
                // 2. 创建请求对象:使用fetch API
                // 3. 配置并发送请求:指定GET方式,带页码参数
                const response = await fetch(`https://jsonplaceholder.typicode.com/photos?_page=${currentPage}&_limit=${pageSize}`);
                
                if (!response.ok) {
                    throw new Error(`请求失败: ${response.status}`);
                }
                
                // 4. 服务器处理请求后返回数据,这里解析JSON
                const products = await response.json();
                
                // 移除加载提示
                const loadingEl = productList.querySelector('.loading');
                if (loadingEl) productList.removeChild(loadingEl);

                // 5. 前端处理响应:生成HTML结构
                if (products.length > 0) {
                    let html = '';
                    products.forEach(product => {
                        html += `
                            <div class="product-card">
                                <img src="${product.thumbnailUrl}" alt="${product.title}">
                                <h3>${product.title.substring(0, 20)}...</h3>
                                <p>商品ID: ${product.id}</p>
                            </div>
                        `;
                    });
                    
                    // 6. 局部更新DOM:将生成的HTML插入到列表
                    productList.insertAdjacentHTML('beforeend', html);
                    
                    // 页码加1,准备下次加载
                    currentPage++;
                    loadMoreBtn.disabled = false;
                    loadMoreBtn.textContent = '加载更多';
                } else {
                    // 没有更多数据
                    loadMoreBtn.textContent = '没有更多商品了';
                }
            } catch (error) {
                // 处理错误
                const loadingEl = productList.querySelector('.loading');
                if (loadingEl) loadingEl.textContent = `加载失败: ${error.message}`;
                
                // 允许重试
                loadMoreBtn.disabled = false;
                loadMoreBtn.textContent = '重试';
            } finally {
                isLoading = false;
            }
        }
    </script>
</body>
</html>
    
相关推荐
fruge3653 小时前
从零到一:我在 Rokid Glasses 上“画”出一个远程协作系统
前端
BumBle3 小时前
高频扫码场景下的去重与接口调用方案
前端·javascript
Mapmost3 小时前
半透明模型渲染全白?Mapmost Studio一招搞定
前端
SpiderPex3 小时前
JavaWeb登录模块完整实现解析:从前端点击到后端验证的全流程
前端
Qhappy3 小时前
某里连线九宫格图片wasm解密&识别
javascript
可乐爱宅着3 小时前
在VSCode的next.js项目中更好的使用eslint/prettier工具
前端·next.js
_大学牲3 小时前
🫡我在掘金写文章:一气之下开源 视频转无水印GIF 插件
前端·javascript
地方地方3 小时前
深入理解 instanceof 操作符:从原理到手动实现
前端·javascript
哀木4 小时前
useRef 为什么不能作为 useEffect 的依赖项
前端