技术演进中的开发沉思-220 Ajax:XMLHttpRequest 对象

在 Web 的早期岁月里,页面与服务器的交互是一种 "粗暴" 的全页刷新模式。用户的每一次操作,无论是提交表单还是点击链接,都意味着浏览器要丢弃当前的一切,重新加载一个全新的页面。这种模式不仅体验糟糕,也造成了大量不必要的数据传输。

直到 XMLHttpRequest 对象的出现,这一切才得以改变。它就像一位隐形的信使 ,能够在不打扰用户、不刷新页面的情况下,悄悄地在浏览器与服务器之间穿梭,传递数据。这便是我们熟知的 AJAX (Asynchronous JavaScript and XML)技术的核心。正是这位信使,构建起了前端与后端之间的数据桥梁,催生了现代富交互 Web 应用(Rich Internet Application)的繁荣。

一、创建与兼容性

要派遣这位信使,首先需要在 JavaScript 中创建它的实例。

javascript 复制代码
let xhr;
// 现代浏览器的标准方式
xhr = new XMLHttpRequest();

然而,在 Web 发展的长河中,总有一些历史的包袱需要背负。对于古老的 IE6 及以下浏览器,它们并不认识 XMLHttpRequest,我们需要通过一个不同的 "咒语" 来召唤它 ------ActiveXObject

javascript 复制代码
// 兼容旧版 IE 的方式
xhr = new ActiveXObject('Microsoft.XMLHTTP');

因此,一个健壮的 "召唤仪式" 通常会结合这两种方式,并使用 try...catch 来捕获可能的错误,确保在不同的浏览器环境下都能成功创建信使。

javascript 复制代码
function createXHR() {
    let xhr;
    try {
        // 尝试召唤现代信使
        xhr = new XMLHttpRequest();
    } catch (e) {
        try {
            // 如果失败,尝试召唤旧版 IE 的信使
            xhr = new ActiveXObject('Microsoft.XMLHTTP');
        } catch (e2) {
            // 如果所有尝试都失败,则说明浏览器过于古老
            xhr = null;
            alert('您的浏览器不支持 AJAX,请升级!');
        }
    }
    return xhr;
}

const xhr = createXHR();
if (xhr) {
    // 信使已就绪,可以开始执行任务
}

二、核心方法

一旦信使(xhr 对象)被成功创建,我们就需要为它规划任务。这主要通过几个核心方法来完成。

open(method, url, async, [user], [password]):规划行程

这个方法用于初始化一个请求,但它并不会立即发送。它更像是在给信使下达任务指令:

  • method : 请求的 "交通方式",通常是 GETPOSTGET 用于从服务器获取数据,POST 用于向服务器提交数据。
  • url: 请求的 "目的地",即服务器上的接口地址,可以是相对路径或绝对路径。
  • async : 是否采用 "异步" 方式。这是 AJAX 的灵魂所在。
    1. true (默认): 异步请求。信使出发后,主线程(浏览器)不会等待,而是继续执行后续代码。当信使带回数据后,会通过回调函数通知主线程。这保证了页面的流畅性。
    2. false: 同步请求。主线程会一直等待,直到信使完成任务并返回数据。在此期间,页面会 "冻结",用户无法进行任何操作。这种方式现在已不被推荐。
  • user, password: 可选参数,用于需要身份验证的请求。
javascript 复制代码
// 初始化一个异步的 GET 请求
xhr.open('GET', 'https://api.example.com/data', true);
setRequestHeader(header, value):准备 "通关文牒"

在发送请求前,有时需要为信使准备一些特殊的 "通关文牒",也就是 HTTP 请求头。

  • 当使用 POST 方法提交表单数据时,必须告诉服务器我们发送的数据格式:

    javascript 复制代码
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  • 还可以设置其他头信息,例如接受的数据类型、Cookie 等。

这个方法必须在 open() 之后、send() 之前调用。

send(body):派遣信使

这是真正发送请求 的方法。它会将请求打包并发送到 open() 方法指定的 URL。

  • body : 要发送给服务器的数据主体。
    • 对于 GET 请求,数据通常通过 URL 的查询字符串(?key=value)发送,因此 send() 方法的参数应为 null
    • 对于 POST 请求,数据通过请求体(body)发送。你需要将数据拼接成字符串(如 'username=admin&password=123')或传递一个 FormData 对象。
javascript 复制代码
// 发送 GET 请求
xhr.send(null);

// 发送 POST 请求
const postData = 'username=admin&password=123';
xhr.open('POST', 'https://api.example.com/login', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(postData);

发送异步请求后,send() 方法会立即返回,程序继续执行。

abort():召回信使

在某些情况下(例如用户点击了 "取消" 按钮,或请求超时),你可能需要中途取消一个正在进行的请求。

javascript 复制代码
// 取消当前请求
xhr.abort();

调用后,xhr 对象的 readyState 会重置为 0(未初始化),请求被终止。

三、 核心属性与事件

对于异步请求,我们无法预知信使何时能返回。因此,我们需要一种机制来 "监听" 它的旅程状态。

readyState:信使的行程状态

xhr.readyState 属性用来表示 请求 / 响应过程的当前活动阶段 。它的值从 0 变化到 4

  • 0 (UNSENT): 请求未初始化。open() 方法尚未被调用。
  • 1 (OPENED): open() 方法已调用,但 send() 方法尚未调用。可以设置请求头。
  • 2 (HEADERS_RECEIVED): send() 方法已调用,服务器的响应头和状态码已收到。
  • 3 (LOADING): 正在接收服务器返回的响应主体(response body)。数据可能还未完全接收完毕。
  • 4 (DONE): 请求已完成,响应数据已完全接收。
onreadystatechange:状态变化的 "警报器"

这是一个事件处理函数 。每当 readyState 属性的值发生变化时,浏览器就会触发这个事件。这是我们处理异步响应的核心。

我们通常会在这里检查 readyState 是否为 4(请求完成),并进一步检查 HTTP 状态码是否为 200(成功)。

javascript 复制代码
xhr.onreadystatechange = function() {
    // 当请求完成 (readyState === 4) 且响应成功 (status === 200)
    if (xhr.readyState === 4 && xhr.status === 200) {
        // 在这里处理服务器返回的数据
        console.log('请求成功!');
        const responseData = xhr.responseText; // 获取文本形式的响应
        // ...
    } else if (xhr.readyState === 4) {
        // 请求完成但失败
        console.error('请求失败,状态码:', xhr.status);
    }
};
status:服务器的 "回信状态"

xhr.status 属性返回服务器响应的 HTTP 状态码。这是判断请求是否成功的关键。

  • 200 OK: 请求成功。
  • 304 Not Modified: 请求的资源未被修改,可以使用浏览器缓存。
  • 400 Bad Request: 客户端请求有语法错误。
  • 401 Unauthorized: 请求要求用户身份验证。
  • 403 Forbidden: 服务器拒绝执行请求。
  • 404 Not Found: 请求的资源不存在。
  • 500 Internal Server Error: 服务器内部发生错误。
responseTextresponseXML:信使带回的 "信物"

当请求成功后,服务器返回的数据就是信使带回的 "信物",可以通过以下两个属性获取:

  • xhr.responseText : 以字符串 形式返回响应数据。这是最常用的方式,返回的通常是 JSON 格式的字符串,需要用 JSON.parse() 解析。
  • xhr.responseXML : 如果服务器返回的是格式正确的 XML 文档,该属性会返回一个 XML DOM 对象,可以使用 DOM 方法来操作。
getResponseHeader()getAllResponseHeaders():查看 "回执"

这两个方法用于获取服务器响应头的信息,相当于查看信使带回的 "回执"。

  • xhr.getResponseHeader('Content-Type'): 获取指定名称的响应头的值。
  • xhr.getAllResponseHeaders(): 获取所有响应头信息,以字符串形式返回。

最后小结:

XMLHttpRequest 对象是 Web 开发中一个里程碑式的存在。它打破了传统 Web 应用的交互模式,通过异步数据交换,极大地提升了用户体验,是构建现代单页应用(SPA)和动态网站的基石。

尽管如今 fetch APIaxios 等更现代、更友好的库在很大程度上取代了 XMLHttpRequest 的直接使用,但理解它的工作原理 ------ 从创建实例、初始化请求、发送数据,到监听状态变化、处理响应 ------ 对于每一位前端开发者来说,都是深入理解 Web 通信本质的必经之路。这位 "老派" 的隐形信使,依然在幕后影响着我们每天使用的无数 Web 应用。

相关推荐
GIS之路2 分钟前
GDAL 实现矢量裁剪
前端·python·信息可视化
是一个Bug5 分钟前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu121387 分钟前
React面向组件编程
开发语言·前端·javascript
持续升级打怪中29 分钟前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路33 分钟前
GDAL 实现矢量合并
前端
hxjhnct35 分钟前
React useContext的缺陷
前端·react.js·前端框架
冰暮流星42 分钟前
javascript逻辑运算符
开发语言·javascript·ecmascript
前端 贾公子1 小时前
从入门到实践:前端 Monorepo 工程化实战(4)
前端
菩提小狗1 小时前
Sqlmap双击运行脚本,双击直接打开。
前端·笔记·安全·web安全
前端工作日常1 小时前
我学习到的AG-UI的概念
前端