Skip to content

Ajax 与 XHR 异步通信

本文系统探讨 Ajax (Asynchronous JavaScript and XML) 异步数据交互原理、XMLHttpRequest 核心 API 规范、jQuery 封装的 Ajax 体系、Promise 封装实践以及与 Axios 的对比演进。

1. Ajax 概念与演进

Ajax (Asynchronous JavaScript and XML) 是一种在无需重新加载整个 Web 页面的情况下,能够更新部分网页的技术。

通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新局部刷新,极大提升了 Web 应用的用户体验与响应速度。


2. XMLHttpRequest (XHR) 核心 API 规范

XMLHttpRequest 是实现 Ajax 异步网络通信的基础底层对象。

2.1 基础请求步骤

javascript
// 1. 创建 XMLHttpRequest 实例对象
const xhr = new XMLHttpRequest();

// 2. 配置请求方式、目标 URL 及是否异步 (第三个参数 true 表示异步)
xhr.open('GET', '/api/users?page=1', true);

// 3. 设置响应与状态改变监听
xhr.onreadystatechange = function () {
    // readyState 为 4 表示 HTTP 请求与接收已完全结束
    if (xhr.readyState === 4) {
        // status 为 200 或 304 表示服务器成功返回响应
        if (xhr.status === 200 || xhr.status === 304) {
            // 解析返回的 JSON 数据
            const data = JSON.parse(xhr.responseText);
            console.log('获取数据成功:', data);
        } else {
            console.error('请求失败,状态码:', xhr.status);
        }
    }
};

// 4. 发送 HTTP 请求(POST 请求需传入请求体参数)
xhr.send(null);

2.2 readyState 5 种状态解析

readyState状态名称详细说明
0UNSENTXMLHttpRequest 对象已创建,但尚未调用 open() 方法
1OPENEDopen() 方法已被调用,已建立 HTTP 连接配置
2HEADERS_RECEIVEDsend() 方法已被调用,且已接收到响应头与响应状态码
3LOADING正在接收服务器返回的响应体数据(数据加载中)
4DONE响应数据接收完全完成,此时可安全读取 responseText

3. 基于 Promise 封装轻量 Ajax 客户端

为避免原生 XHR 回调地狱(Callback Hell),可以使用 ES6 Promise 将其重构成支持 async/await 的现代化异步客户端:

javascript
function myAjax({ url, method = 'GET', data = null, headers = {} }) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open(method.toUpperCase(), url, true);

        // 设置默认的 JSON 请求头
        xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
        
        // 绑定自定义请求头
        Object.keys(headers).forEach(key => {
            xhr.setRequestHeader(key, headers[key]);
        });

        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
                    try {
                        const response = JSON.parse(xhr.responseText);
                        resolve(response);
                    } catch (e) {
                        resolve(xhr.responseText);
                    }
                } else {
                    reject(new Error(`HTTP 请求异常: ${xhr.status} ${xhr.statusText}`));
                }
            }
        };

        xhr.onerror = function () {
            reject(new Error('网络连接异常或跨域限制'));
        };

        // 发送数据
        xhr.send(data ? JSON.stringify(data) : null);
    });
}

4. jQuery 中的 Ajax 封装与常用 API

在传统 Web 开发中,jQuery 提供了兼容性极佳且简单易用的 Ajax 封装体系。

4.1 通用底层方法 $.ajax(options)

$.ajax() 是 jQuery 核心的 Ajax 方法,所有的快捷方法底层都基于它实现:

javascript
$.ajax({
    url: '/api/user/info',            // 请求的目标地址
    type: 'POST',                     // 请求方式:GET, POST, PUT, DELETE (也可以写作 method)
    data: JSON.stringify({ id: 101 }),// 发送到服务端的数据
    contentType: 'application/json;charset=utf-8', // 发送数据的 Content-Type
    dataType: 'json',                 // 预期服务端返回的数据类型:json, xml, html, text
    timeout: 5000,                    // 请求超时时间(毫秒)
    headers: {
        'Authorization': 'Bearer my_token'
    },
    beforeSend: function(xhr) {
        // 请求发送前触发(常用于开启 Loading 动画)
        console.log('准备发送 Ajax 请求...');
    },
    success: function(data, status, xhr) {
        // 请求成功回调函数 (jQuery 会依据 dataType 自动解析 JSON)
        console.log('收到响应:', data);
    },
    error: function(xhr, status, error) {
        // 请求失败回调函数
        console.error('Ajax 出错:', status, error);
    },
    complete: function(xhr, status) {
        // 无论成功还是失败都会触发的回调(常用于关闭 Loading 动画)
        console.log('请求完成');
    }
});

4.2 快捷简写 API 方法

为了提高编写效率,jQuery 提供了几个针对特定场景的简写方法:

javascript
// 1. $.get(url, [data], [callback], [dataType])
$.get('/api/search', { keyword: 'phone' }, function(res) {
    console.log('GET 搜索结果:', res);
}, 'json');

// 2. $.post(url, [data], [callback], [dataType])
$.post('/api/login', { username: 'admin', pwd: '123' }, function(res) {
    console.log('登录成功:', res);
});

// 3. $.getJSON(url, [data], [callback])
// 专门用于通过 GET 方式加载 JSON 数据的快捷方法
$.getJSON('/api/config.json', function(data) {
    console.log('读取配置成功:', data);
});

4.3 表单序列化与全局 Ajax 事件

表单快速序列化 serialize()

javascript
// 快速将 <form> 表单内部所有带 name 属性的表单项自动拼接为 query string 格式
// 格式如: "username=alice&email=alice%40163.com"
const formDataString = $('#login-form').serialize();

$.post('/api/user/save', formDataString, function(res) {
    console.log('保存结果:', res);
});

全局 Ajax 事件监听

可以使用全局事件在全页面拦截任何 Ajax 请求的生命周期(如显示全局遮罩):

javascript
$(document).ajaxStart(function() {
    $('#global-loading').show(); // 全局任意 Ajax 开始时显示遮罩
});

$(document).ajaxStop(function() {
    $('#global-loading').hide(); // 全局所有 Ajax 结束时隐藏遮罩
});

5. 跨方案特性对比

技术方案原生 XMLHttpRequestjQuery $.ajaxFetch APIAxios
依赖与体积原生零依赖需引入整个 jQuery 库 (~30KB)原生零依赖轻量第三方库 (~13KB)
API 范式回调事件监听回调函数 / deferred Promise现代标准 Promise现代标准 Promise
JSON 处理手动 JSON.parse自动转为 JS 对象 (依据 dataType)需要 response.json()自动转换为 JS 对象
拦截器支持支持全局 ajaxSetup/全局事件原生 Request/Response 拦截器
主用场景底层原生学习传统 jQuery 全家桶项目原生 JS 现代浏览器项目现代 Vue / React 工程

基于 VitePress 构建 | 技术知识库