Skip to content

XMLHttpRequest

[TOC]

索引

构造方法

  • new XMLHttpRequest()(),用于创建一个 XHR 实例对象。用于与服务器交互,支持异步 HTTP 请求(如 AJAX)。

属性

  • xhr.onreadystatechangefn事件处理,用于监听请求状态变化。会在 readyState 属性值变化时触发,是处理异步请求响应的传统方式。
  • xhr.readyState0|1|2|3|4只读,表示请求的当前状态
  • xhr.statusnumber只读,表示 HTTP 响应的状态码
  • xhr.statusTextstring只读,用于获取 HTTP 响应的状态文本
  • xhr.responseany只读,用于获取 HTTP 响应的内容主体。可以根据 responseType 设置自动解析响应内容。
  • xhr.responseTypestring默认:'text'可读写,用于指定服务器响应的数据类型,并控制如何解析响应数据。
  • xhr.timeoutnumber默认:0可读写,用于设置 HTTP 请求的超时时间(毫秒)。超时后请求自动终止并触发 timeout 事件。
  • xhr.withCredentialsboolean默认:false可读写,用于控制跨域请求是否携带用户凭证(如 cookies、HTTP 认证信息)。

方法

  • xhr.open()(method,url,async?,user?,password?),用于初始化 HTTP 请求配置(但不会发送请求)。需配合 send() 方法使用。
  • xhr.send()(body?),用于实际发送 HTTP 请求到服务器。必须在 open() 之后调用。
  • xhr.abort()(),用于立即终止正在进行的 HTTP 请求。调用后,请求会被取消,不会继续接收响应数据。
  • xhr.setRequestHeader()(header, value),用于在发送请求前设置 HTTP 请求头。确保在 open() 之后、send() 之前调用。
  • xhr.getResponseHeader()(headerName),用于获取指定 HTTP 响应头的值

事件

  • loadstart(event)=>void首个事件,在浏览器开始加载服务器响应时触发。
  • load(event)=>void核心成功事件,当请求成功完成且响应数据完全接收时触发。
  • loadend(event)=>void最终事件,在请求结束时触发(无论成功或失败)。用于清理资源执行最终操作
  • progress
  • abort
  • error
  • timeout

XMLHttpRequest

构造方法

new XMLHttpRequest()@

new XMLHttpRequest()(),用于创建一个 XHR 实例对象。用于与服务器交互,支持异步 HTTP 请求(如 AJAX)。

  • 返回:

  • xhrXMLHttpRequest,返回一个 XHR 对象实例。用于发起 HTTP 请求,包含请求配置、发送和响应处理的方法与属性。

基本示例

  1. 完整工作流程

    js
    // 1. 创建实例
    const xhr = new XMLHttpRequest();
    
    // 2. 配置请求
    xhr.open("GET", "https://api.example.com/data", true);
    
    // 3. 设置请求头(可选)
    xhr.setRequestHeader("Accept", "application/json");
    
    // 4. 定义事件处理
    xhr.onload = function () {
      if (xhr.status >= 200 && xhr.status < 300) {
        // 成功:解析响应
        console.log("响应数据:", xhr.response); 
      } else {
        console.error("请求失败,状态码:", xhr.status);
      }
    };
    
    xhr.onerror = function () {
      console.error("网络错误");
    };
    
    // 5. 发送请求(GET 请求体为空)
    xhr.send();

注意事项

  1. 跨域请求 (CORS)

    需服务器设置响应头(如 Access-Control-Allow-Origin),否则浏览器会阻止响应。

  2. 同步请求已弃用

    设置 async: false 会导致页面阻塞(不推荐)。

  3. 响应类型

    设置 xhr.responseType = "json" 可自动解析 JSON 响应(通过 xhr.response 访问)。

  4. 安全性

    避免向不可信来源发送敏感数据。

  5. 替代方案

    • fetch:更现代、基于 Promise 的替代方案(推荐新项目使用)。
    • Axios:基于 Promise 的第三方 HTTP 库(支持浏览器和 Node.js)。

属性

onreadystatechange@

xhr.onreadystatechangefn事件处理,用于监听请求状态变化。会在 readyState 属性值变化时触发,是处理异步请求响应的传统方式。

  • fn()=>void,事件处理函数。无显式参数,但可通过 this 或闭包访问 XHR 对象。

基本示例

  1. 基础示例

    js
    const xhr = new XMLHttpRequest();
    xhr.open("GET", "https://api.example.com/data", true);
    
    xhr.onreadystatechange = function() {
      console.log(`状态变化: ${this.readyState}`);
      
      if (this.readyState === 4) {
        if (this.status >= 200 && this.status < 300) {
          console.log("响应数据:", this.response);
        } else {
          console.error("请求失败");
        }
      }
    };
    
    xhr.send();

核心特性

  1. 触发时机

    每当 readyState 值改变时触发(包括所有状态变化):

  2. 对比 替代事件

    事件触发时机推荐度
    onreadystatechange所有 readyState 变化△ 传统
    onload请求成功完成 (readyState=4)★★★
    onerror请求失败时★★★
    onprogress数据传输过程中周期性触发★★☆
    js
    xhr.onload = function() {
      if (this.status === 200) {
        console.log(this.response);
      }
    };
    
    xhr.onerror = function() {
      console.error("网络错误");
    };

进阶示例

  1. 结合进度事件

    js
    xhr.onreadystatechange = function() {
      if (this.readyState === 3) { // 正在接收数据
        console.log("已接收部分数据");
      }
    };
    
    xhr.onprogress = function(event) {
      if (event.lengthComputable) {
        const percent = Math.round((event.loaded / event.total) * 100);
        console.log(`进度: ${percent}%`);
      }
    };
  2. 事件移除:赋值为 null 可移除事件监听

    js
    // 移除事件监听
    xhr.onreadystatechange = null;
    
    // 一次性事件
    xhr.onreadystatechange = function handler() {
      if (this.readyState === 4) {
        // 执行操作...
        xhr.onreadystatechange = null; // 移除自身
      }
    };

readyState@

xhr.readyState0|1|2|3|4只读,表示请求的当前状态

核心特性

  1. 状态机详解

    1. 0: UNSENT (未初始化)

      触发条件

      • XMLHttpRequest 对象刚创建
      • 调用 abort() 方法后
      js
      // XMLHttpRequest 对象刚创建
      const xhr = new XMLHttpRequest();
      console.log(xhr.readyState); // 0
      
      // 调用 `abort()` 方法后
      xhr.abort();
      console.log(xhr.readyState); // 0 (UNSENT)
      console.log(xhr.status); // 0
    2. 1: OPENED (已打开)

      触发条件:成功调用 open() 方法后

      可用操作

      • 设置请求头 setRequestHeader()
      • 配置超时 timeout
      • 设置凭证 withCredentials
      js
      xhr.open("GET", "/api/data", true);
      console.log(xhr.readyState); // 1
    3. 2: HEADERS_RECEIVED (头信息已接收)

      触发条件:接收到服务器响应头

      可用属性

      • status: HTTP 状态码
      • statusText: 状态文本
      • getResponseHeader()
      • getAllResponseHeaders()
      js
      xhr.onreadystatechange = function() {
        if (this.readyState === 2) {
          console.log("状态码:", this.status);
          console.log("Content-Type:", this.getResponseHeader("Content-Type"));
        }
      };
    4. 3: LOADING (数据加载中)

      触发条件:开始接收响应体

      关键特性

      • 周期性触发(可能多次)
      • 可访问部分接收的数据

      可用属性

      • responseText(部分数据)
      • response(部分数据)
      js
      xhr.onreadystatechange = function() {
        if (this.readyState === 3) {
          console.log("已接收部分数据:", this.responseText.slice(0, 50));
        }
      };
    5. 4: DONE (请求完成)

      触发条件:请求操作完成(无论成功或失败)

      关键检查

      • status: HTTP 状态码
      • statusText: 状态描述

      可用属性

      • responseText: 完整响应文本
      • response: 根据 responseType 解析的响应
      • responseXML: XML 响应
      js
      xhr.onreadystatechange = function() {
        if (this.readyState === 4) {
          if (this.status === 200) {
            console.log("完整响应:", this.response);
          } else {
            console.error("请求失败:", this.status);
          }
        }
      };
  2. 同步请求的状态变化:同步请求中状态变化在 send() 后立即完成

    js
    // 同步请求中状态变化在 send() 后立即完成
    xhr.open("GET", "/data", false); // 同步模式
    console.log(xhr.readyState); // 1 (OPENED)
    xhr.send();
    console.log(xhr.readyState); // 4 (DONE) 直接跳转

status@

xhr.statusnumber只读,表示 HTTP 响应的状态码

基本示例

  1. 基础状态检查

    js
    xhr.onreadystatechange = function() {
      if (this.readyState === 4) {
        // 主成功范围 (200-299)
        if (this.status >= 200 && this.status < 300) {
          console.log("请求成功");
        } else {
          console.error(`请求失败: ${this.status} ${this.statusText}`);
        }
      }
    };

核心特性

  1. HTTP 状态码

    值范围类别典型值示例说明
    100-199信息响应101, 103临时响应,很少使用
    200-299成功响应200, 201, 204请求成功处理
    300-399重定向301, 302, 304需要进一步操作
    400-499客户端错误400, 401, 403, 404请求包含错误或无法完成
    500-599服务器错误500, 502, 503服务器处理请求失败
    特殊值
    0非 HTTP 错误-请求未发送或完全失败
  2. 特殊状态 0

    出现场景:

    • 请求被取消 (xhr.abort())
    • 网络连接断开
    • 跨域请求被浏览器阻止
    • 无效 URL 或协议错误
    js
    xhr.onerror = function() {
      if (this.status === 0) {
        console.error("请求失败:网络错误或请求被阻止");
      }
    };

进阶示例

  1. 完整工作流

    js
    const xhr = new XMLHttpRequest();
    xhr.open("GET", "https://api.example.com/data", true);
    
    // 配置响应类型
    xhr.responseType = "json";
    
    xhr.onload = function() {
      if (this.status === 0) {
        showError("网络连接失败");
        return;
      }
    
      if (this.status >= 200 && this.status < 300) {
        displayData(this.response);
      } else if (this.status === 401) {
        showLoginPrompt();
      } else if (this.status === 404) {
        showNotFoundMessage();
      } else if (this.status >= 500) {
        showServerError();
      } else {
        showError(`未知错误: HTTP ${this.status}`);
      }
    };
    
    xhr.onerror = function() {
      showError("请求失败: " + (this.status || "未知错误"));
    };
    
    xhr.send();

statusText

xhr.statusTextstring只读,用于获取 HTTP 响应的状态文本

基本示例

  1. 基础状态处理

    js
    xhr.onreadystatechange = function() {
      if (this.readyState === 4) { // 请求完成
        console.log(`HTTP ${this.status}: ${this.statusText}`);
        
        if (this.status >= 200 && this.status < 300) {
          console.log("成功:", this.statusText);
        } else if (this.status >= 400 && this.status < 500) {
          console.error("客户端错误:", this.statusText);
        } else if (this.status >= 500) {
          console.error("服务器错误:", this.statusText);
        }
      }
    };

核心特性

  1. 与 HTTP 状态码的对应关系

    xhr.statusTextxhr.status 配合使用,提供完整的 HTTP 状态信息:

    js
    xhr.onload = function() {
      console.log(`状态码: ${this.status}`);      // 200
      console.log(`状态文本: ${this.statusText}`); // "OK"
      
      if (this.status === 200) {
        console.log("请求成功:", this.statusText); // "请求成功: OK"
      }
    };
  2. HTTP 状态码

  3. 推荐基于状态码判断:基于状态文本判断可能不可靠

response@

xhr.responseany只读,用于获取 HTTP 响应的内容主体。可以根据 responseType 设置自动解析响应内容。

核心特性

  1. 结合 responseType

    js
    // 1. 设置响应类型为 JSON
    xhr.responseType = "json";
    
    xhr.onload = function() {
      // 2. 自动解析为 JS 对象
      console.log(this.response.user.name);
    };
  2. 对比 其他响应属性

    属性返回类型特点
    response动态类型根据 responseType 变化
    responseTextstring仅文本内容,忽略二进制
    responseXMLDocument仅 XML/HTML 内容,需有效文档
    responseURLstring最终响应 URL (处理重定向后)

进阶示例

  1. 文本响应处理

    js
    xhr.responseType = "text";
    xhr.onload = function() {
      console.log("文本内容:", this.response);
    };
  2. JSON 数据处理

    js
    xhr.responseType = "json";
    xhr.onload = function() {
      if (this.status === 200) {
        console.log("用户:", this.response.name);
      } else {
        // 错误处理
        console.error("错误数据:", this.response);
      }
    };
  3. 二进制数据处理

    js
    // 接收 ArrayBuffer
    xhr.responseType = "arraybuffer";
    xhr.onload = function() {
      const buffer = this.response;
      const view = new Uint8Array(buffer);
      console.log("二进制数据:", view);
    };
    js
    // 接收 Blob (如图片)
    xhr.responseType = "blob";
    xhr.onload = function() {
      const img = document.createElement("img");
      img.src = URL.createObjectURL(this.response);
      document.body.appendChild(img);
    };
  4. XML 文档处理

    js
    xhr.responseType = "document";
    xhr.overrideMimeType("text/xml"); // 确保解析为 XML
    
    xhr.onload = function() {
      const xmlDoc = this.response;
      const title = xmlDoc.querySelector("title").textContent;
      console.log("XML 标题:", title);
    };

responseType@

xhr.responseTypestring默认:'text'可读写,用于指定服务器响应的数据类型,并控制如何解析响应数据。

基本示例

  1. 自动数据解析

    responseType 的主要价值在于自动数据解析

    js
    // 传统方式:手动解析
    xhr.onload = function() {
      if (this.status === 200) {
        const data = JSON.parse(this.responseText); // 需要手动解析
        processData(data);
      }
    };
    js
    // 现代方式:自动解析
    xhr.responseType = "json";
    xhr.onload = function() {
      if (this.status === 200) {
        processData(this.response); // 已经是 JavaScript 对象
      }
    };

核心特性

  1. responseType 接受值

    类型说明对应响应属性
    "" (空字符串)string默认值,响应文本responseText
    "text"string响应文本responseText
    "json"Object自动解析的 JS 对象response
    "arraybuffer"ArrayBuffer二进制数据的原始缓冲区response
    "blob"Blob二进制大对象(文件等)response
    "document"DocumentXML/HTML 文档对象responseXML

进阶示例

  1. 文本数据处理 ("""text")

    js
    xhr.responseType = "text"; // 或 ""
    xhr.onload = function() {
      if (this.status === 200) {
        const text = this.response; // 或 this.responseText
        console.log("文本内容:", text);
        
        // 处理纯文本、CSV、自定义格式等
        if (text.startsWith("<?xml")) {
          // 检测到XML,可以重新解析
          const parser = new DOMParser();
          const xmlDoc = parser.parseFromString(text, "text/xml");
        }
      }
    };
  2. JSON 数据处理 ("json")

    js
    xhr.responseType = "json";
    xhr.onload = function() {
      if (this.status === 200) {
        const data = this.response; // 自动解析的JS对象
        
        // 直接使用对象属性
        console.log("用户:", data.user.name);
        console.log("邮件:", data.user.email);
        
        // 不需要 try-catch JSON.parse()
      } else {
        // 错误响应也可能是JSON格式
        if (this.response && this.response.error) {
          console.error("服务器错误:", this.response.error.message);
        }
      }
    };
    
    xhr.onerror = function() {
      // 网络错误时 response 为 null
      console.log(this.response); // null
    };
  3. 二进制数据处理 ("arraybuffer", "blob")

    js
    // 使用 ArrayBuffer 处理原始二进制数据
    xhr.responseType = "arraybuffer";
    xhr.onload = function() {
      if (this.status === 200) {
        const buffer = this.response;
        const view = new Uint8Array(buffer);
        
        // 处理二进制协议、图像数据等
        console.log("二进制数据长度:", buffer.byteLength);
        
        // 检测文件类型
        if (view[0] === 0x89 && view[1] === 0x50) { // PNG 文件头
          console.log("PNG 图像");
        }
      }
    };
    js
    // 使用 Blob 处理文件下载
    xhr.responseType = "blob";
    xhr.onload = function() {
      if (this.status === 200) {
        const blob = this.response;
        const url = URL.createObjectURL(blob);
        
        // 创建下载链接
        const a = document.createElement("a");
        a.href = url;
        a.download = "file.pdf";
        document.body.appendChild(a);
        a.click();
        
        // 清理
        setTimeout(() => {
          URL.revokeObjectURL(url);
          document.body.removeChild(a);
        }, 100);
      }
    };
  4. XML 文档处理 ("document")

    js
    xhr.responseType = "document";
    xhr.overrideMimeType("text/xml"); // 确保解析为XML
    
    xhr.onload = function() {
      if (this.status === 200) {
        const xmlDoc = this.response; // 或 this.responseXML
        
        // 使用 DOM API 操作
        const title = xmlDoc.querySelector("title").textContent;
        const items = xmlDoc.querySelectorAll("item");
        
        console.log("文档标题:", title);
        items.forEach(item => {
          console.log("项目:", item.textContent);
        });
      }
    };

timeout

xhr.timeoutnumber默认:0可读写,用于设置 HTTP 请求的超时时间(毫秒)。超时后请求自动终止并触发 timeout 事件。

基本示例

  1. 基础设置

    js
    const xhr = new XMLHttpRequest();
    xhr.open("GET", "https://api.example.com/data", true);
    
    // 1. 设置5秒超时
    xhr.timeout = 5000; 
    
    // 2. 监听超时事件
    xhr.ontimeout = function() {
      console.error("请求超时:服务器未在5秒内响应");
    };
    
    xhr.send();

核心特性

  1. 默认值:0:表示无超时限制

withCredentials@

xhr.withCredentialsboolean默认:false可读写,用于控制跨域请求是否携带用户凭证(如 cookies、HTTP 认证信息)。

基本示例

  1. 基础跨域认证

    js
    const xhr = new XMLHttpRequest();
    xhr.open("GET", "https://api.example.com/user", true);
    
    // 启用凭证携带
    xhr.withCredentials = true;
    
    xhr.onload = function() {
      if (this.status === 200) {
        console.log("用户数据:", this.response);
      }
    };
    
    xhr.send();

核心特性

  1. 凭证包含内容

    当设置为 true 时,请求会携带:

    • Cookies(包括会话 cookie)
    • HTTP 认证头(Authorization)
    • TLS 客户端证书
    • Windows 域认证(IE 特有)
  2. CORS 协作要求

    image-20250820223949007

  3. 服务器头要求

    响应头必需值说明
    Access-Control-Allow-Credentialstrue明确允许凭证
    Access-Control-Allow-Origin具体源 (非*)必须与请求源完全匹配
    Access-Control-Expose-Headers需暴露的自定义头否则客户端无法访问
  4. 服务器 Cookie 属性要求

    • 不能设置 SameSite=None; Secure(需要 HTTPS)
    • 推荐 HttpOnly 增强安全
    js
    // 服务器设置安全 Cookie
    res.cookie("session", token, {
      httpOnly: true,    // 阻止 JS 访问
      secure: true,      // 仅 HTTPS
      sameSite: "None",  // 允许跨域
      domain: ".example.com",
      maxAge: 24 * 60 * 60 * 1000 // 1天
    });
  5. 客户端响应访问限制:即使响应成功,也可能无法访问某些头(如 Set-Cookie),需要服务器端暴露头

    js
    xhr.withCredentials = true;
    xhr.onload = function() {
      // 即使成功,也可能无法访问某些头
      console.log(this.getResponseHeader("Set-Cookie")); // null
    };
  6. 同源请求:自动携带凭证(忽略此设置)

进阶示例

  1. 结合 Cookie 使用

    js
    // 客户端设置
    xhr.withCredentials = true;
    js
    // 服务器端要求 (Node.js示例)
    app.get("/data", (req, res) => {
      res.header("Access-Control-Allow-Origin", "https://client.com");
      res.header("Access-Control-Allow-Credentials", "true");
      res.cookie("session", "token123", { httpOnly: true });
      res.json({ user: "John" });
    });
  2. 预检请求处理:复杂请求触发预检 (OPTIONS)

    js
    // 复杂请求触发预检 (OPTIONS)
    xhr.open("POST", "https://api.example.com/data", true);
    xhr.setRequestHeader("X-Custom-Header", "value");
    xhr.withCredentials = true;
    js
    // 服务器需响应
    // OPTIONS 响应头:
    //   Access-Control-Allow-Headers: X-Custom-Header
    //   Access-Control-Allow-Credentials: true
  3. 完整工作流

    js
    // 客户端代码
    function fetchAuthData() {
      const xhr = new XMLHttpRequest();
      xhr.open("GET", "https://api.secure.com/user/profile", true);
      
      // 关键设置
      xhr.withCredentials = true;
      xhr.responseType = "json";
      
      xhr.onload = function() {
        if (this.status === 200) {
          displayUserProfile(this.response);
        } else if (this.status === 401) {
          showLoginModal();
        }
      };
      
      xhr.onerror = function() {
        if (this.status === 0) {
          handleNetworkError();
        }
      };
      
      xhr.send();
    }
    js
    // 服务器端 (Node.js/Express)
    app.get("/user/profile", (req, res) => {
      // 必需 CORS 头
      res.header("Access-Control-Allow-Origin", "https://client-app.com");
      res.header("Access-Control-Allow-Credentials", "true");
      
      // 验证 cookie
      const sessionCookie = req.cookies.session;
      if (!validateSession(sessionCookie)) {
        return res.status(401).json({ error: "未认证" });
      }
      
      // 返回敏感数据
      res.json({
        name: "张三",
        email: "zhangsan@example.com",
        permissions: ["read:profile", "write:settings"]
      });
    });

方法

open()@

xhr.open()(method,url,async?,user?,password?),用于初始化 HTTP 请求配置(但不会发送请求)。需配合 send() 方法使用。

  • methodGET|POST|DELETE|PUT|PATCH|OPTIONS|HEAD,HTTP 请求方法。

  • urlstring,请求的目标 URL(支持相对路径或绝对路径)。

  • async?boolean默认:true,是否异步执行(同步已废弃)。

  • user?string默认:null,HTTP 基础认证的用户名。

  • password?string默认:null,HTTP 基础认证的密码。

基本示例

  1. 初始化请求

    js
    const xhr = new XMLHttpRequest();
    xhr.open("POST", "/submit", true); // 异步 POST 请求

核心特性

  1. 调用顺序

    • 必须在 .send() 前调用
    • 必须在 .setRequestHeader() 前调用
  2. 跨域请求 (CORS)

    • 需服务器设置 Access-Control-Allow-Origin

    • 带认证的跨域请求需设置:

      js
      xhr.withCredentials = true; // 发送 cookies
  3. 异步 vs 同步

    同步请求已被废弃:会导致浏览器主线程阻塞,用户界面卡顿。

    js
    // 异步请求(推荐)
    xhr.open("GET", "/data", true);
    
    // 同步请求(已废弃!阻塞页面执行)
    xhr.open("GET", "/data", false); // ⚠️ 避免使用
  4. URL 处理

    js
    // 相对路径 → 自动补全为当前域名
    xhr.open("GET", "/api/users"); 
    
    // 绝对路径
    xhr.open("POST", "https://api.example.com/data");
    
    // 查询参数(需手动编码)
    const params = new URLSearchParams({ id: 123 });
    xhr.open("GET", `/api?${params}`);
  5. HTTP 认证:带认证的请求

    仅当服务器要求基础认证时使用(通过 WWW-Authenticate 响应头触发)

    安全警告:密码以明文传输,必须使用 HTTPS

    js
    // 带认证的请求
    xhr.open("GET", "/protected", true, "admin", "pass123");

send()@

xhr.send()(body?),用于实际发送 HTTP 请求到服务器。必须在 open() 之后调用。

  • body?FormData|null|URLSearchParams|Arraybuffer|Blob|Document|DOMString默认:null,作为请求主体发送的数据。对于 GET/HEAD 请求应为 null 或省略。

基本示例

  1. GET 请求(无请求体)

    js
    xhr.open("GET", "/api/data", true);
    xhr.send(); // 无参数
  2. POST 请求(发送字符串)

    js
    xhr.open("POST", "/submit", true);
    xhr.setRequestHeader("Content-Type", "application/json");
    xhr.send(JSON.stringify({ name: "John", age: 30 }));
  3. POST 请求(发送 FormData 表单提交)

    js
    const formData = new FormData();
    formData.append("username", "john_doe");
    formData.append("avatar", fileInput.files[0]);
    
    xhr.open("POST", "/upload", true);
    xhr.send(formData); // 自动设置 Content-Type
  4. POST 请求(发送二进制数据 Blob/ArrayBuffer)

    js
    // 发送 Blob(如图片)
    const blob = new Blob([binaryData], { type: "image/png" });
    xhr.open("POST", "/upload-image", true);
    xhr.send(blob);
    js
    // 发送 ArrayBuffer
    const buffer = new ArrayBuffer(8);
    xhr.open("POST", "/upload-binary", true);
    xhr.send(buffer);
  5. POST 请求(发送 URL 编码数据)

    js
    const params = new URLSearchParams();
    params.append("key1", "value1");
    params.append("key2", "value2");
    
    xhr.open("POST", "/submit-form", true);
    xhr.send(params); // 自动设置 Content-Type

核心特性

  1. 常见请求体类型
    • null 或省略:无请求体(GET 请求默认)
    • DOMString:普通字符串(需手动设置 Content-Type
    • FormData:表单数据(自动设置 multipart/form-data
    • Blob:二进制大对象(如文件)
    • ArrayBuffer:原始二进制数据
    • URLSearchParams:查询字符串格式(自动设置 application/x-www-form-urlencoded
    • Document:XML 文档
  2. 调用顺序send() 方法必须在最后调用

abort()

xhr.abort()(),用于立即终止正在进行的 HTTP 请求。调用后,请求会被取消,不会继续接收响应数据。

基本示例

  1. 完整示例

    js
    const xhr = new XMLHttpRequest();
    let isRequestAborted = false;
    
    // 初始化请求
    xhr.open("GET", "https://api.example.com/large-data", true);
    
    // 事件处理
    xhr.onabort = () => {
      isRequestAborted = true;
      console.log("请求已被用户取消");
    };
    
    xhr.onload = () => {
      if (!isRequestAborted) {
        console.log("请求成功:", xhr.response);
      }
    };
    
    xhr.onerror = () => {
      if (!isRequestAborted) {
        console.error("请求失败");
      }
    };
    
    // 发送请求
    xhr.send();
    
    // 用户取消按钮
    document.getElementById("cancelBtn").addEventListener("click", () => {
      if (xhr.readyState > 0 && xhr.readyState < 4) {
        xhr.abort();
      }
    });

核心行为

  1. 终止请求

    • 立即停止进行中的请求传输
    • 停止接收服务器响应
    • readyState 重置为 0 (UNSENT)
    • 设置 status0
  2. 触发事件

    • 触发 abort 事件
    • 触发 readystatechange 事件(状态变为 0
  3. 后续处理

    • 可以安全地重用同一个 XMLHttpRequest 对象
    • 需要重新调用 open()send() 发起新请求

进阶示例

  1. 复用请求对象:终止后可以重新配置并发送新请求

    js
    // 终止后可以重用
    xhr.abort();
    
    // 重新配置并发送新请求
    xhr.open("GET", "/new-data", true);
    xhr.send();

setRequestHeader()@

xhr.setRequestHeader()(header, value),用于在发送请求前设置 HTTP 请求头。确保在 open() 之后、send() 之前调用。

  • headerstring,HTTP 请求头的名称(不区分大小写)

  • valuestring,请求头的值

基本示例

  1. 设置请求头

    js
    // 设置内容类型
    xhr.setRequestHeader("Content-Type", "application/json");
    
    // 设置认证令牌
    xhr.setRequestHeader("Authorization", "Bearer token123");
    
    // 设置自定义头
    xhr.setRequestHeader("X-Custom-Header", "value");

核心特性

  1. 受限制的请求头

    浏览器禁止设置以下安全相关的头(设置会被静默忽略):

    • Host
    • Connection
    • Keep-Alive
    • Accept-Encoding
    • Cookie(使用 xhr.withCredentials 代替)
    • Content-Length
    • 等(完整列表见 MDN 禁止的请求头)
  2. 自动设置的头

    浏览器会自动设置:

    • Content-Length(根据请求体)
    • User-Agent
    • Referer
    • Origin(跨域请求时)
  3. 多次调用效果

    • 相同头名称的多次设置会合并值(逗号分隔)而非覆盖:

      js
      xhr.setRequestHeader("Accept", "application/json");
      xhr.setRequestHeader("Accept", "text/xml");
      
      // 实际发送:Accept: application/json, text/xml
    • 需要覆盖值时需先移除原有头(无法直接覆盖)

  4. 自定义请求头

    • 自定义请求头会触发预检请求 (OPTIONS)

    • 服务器需响应相应头:

      http
      Access-Control-Allow-Headers: Content-Type, X-Custom-Header
  5. 携带 Cookie

    携带凭据需设置:

    js
    xhr.withCredentials = true;

进阶示例

  1. 使用流程

    js
    // 1. 创建实例
    const xhr = new XMLHttpRequest();
    
    // 2. 初始化请求(必须在 setRequestHeader 之前)
    xhr.open("POST", "/api/submit", true);
    
    // 3. 设置请求头(必须在此位置)
    xhr.setRequestHeader("Content-Type", "application/json");
    xhr.setRequestHeader("X-Request-ID", "uuid12345");
    
    // 4. 处理响应
    xhr.onload = function() {
      // ...
    };
    
    // 5. 发送请求
    xhr.send(JSON.stringify({ data: "test" }));

getResponseHeader()@

xhr.getResponseHeader()(headerName),用于获取指定 HTTP 响应头的值

  • headerNamestring,要获取的 HTTP 响应头名称(不区分大小写)。

  • 返回:

  • headerstring|null, 返回响应头的值(如果存在且可访问);响应头不存在、值为空或浏览器禁止访问返回 null。

基本示例

  1. 基本用法

    js
    xhr.onreadystatechange = function() {
      if (this.readyState === 4 && this.status === 200) {
        // 获取单个响应头
        const contentType = this.getResponseHeader("Content-Type");
        const contentLength = this.getResponseHeader("Content-Length");
        
        console.log("内容类型:", contentType); // "application/json; charset=utf-8"
        console.log("内容长度:", contentLength); // "1024"
      }
    };

核心特性

  1. 访问时机:在 readyState ≥ 2 时访问

    js
    // 错误:在请求完成前访问
    xhr.send();
    console.log(xhr.getResponseHeader("Content-Type")); // null
    
    // 正确:在 readyState ≥ 2 时访问
    xhr.onreadystatechange = function() {
      if (this.readyState >= 2) { // HEADERS_RECEIVED
        console.log(this.getResponseHeader("Content-Type")); // 可用
      }
    };
  2. 跨域限制 (CORS)

    • 跨域请求时,默认只能访问简单响应头:

      • Cache-Control
      • Content-Language
      • Content-Type
      • Expires
      • Last-Modified
      • Pragma
    • 若想访问,服务器需设置 Access-Control-Expose-Headers 来暴露其他头

      http
      Access-Control-Expose-Headers: X-Custom-Header, X-Another-Header, Set-Cookie
    js
    xhr.onload = function() {
      // 默认不可访问
      const customHeader = this.getResponseHeader("X-Custom-Header"); // null
      
      // 服务器暴露后可访问
      const exposedHeader = this.getResponseHeader("X-Exposed-Header"); // "value"
    };
  3. 头名称大小写不敏感

    js
    // 头名称大小写不敏感
    xhr.getResponseHeader("content-type") === 
    xhr.getResponseHeader("Content-Type") === 
    xhr.getResponseHeader("CONTENT-TYPE"); // true

进阶示例

  1. 完整工作流

    js
    function makeRequest(url) {
      const xhr = new XMLHttpRequest();
      xhr.open("GET", url, true);
      
      // 设置期望的响应类型
      xhr.responseType = "json";
      
      xhr.onreadystatechange = function() {
        if (this.readyState === 2) { // HEADERS_RECEIVED
          // 检查内容类型
          const contentType = this.getResponseHeader("Content-Type");
          if (contentType && !contentType.includes("application/json")) {
            console.warn("期望JSON但收到:", contentType);
          }
          
          // 检查缓存头
          const cacheControl = this.getResponseHeader("Cache-Control");
          if (cacheControl && cacheControl.includes("no-cache")) {
            console.log("响应不可缓存");
          }
        }
        
        if (this.readyState === 4) {
          if (this.status === 200) {
            // 处理速率限制头(常见于API)
            const rateLimit = this.getResponseHeader("X-RateLimit-Limit");
            const rateRemaining = this.getResponseHeader("X-RateLimit-Remaining");
            const rateReset = this.getResponseHeader("X-RateLimit-Reset");
            
            if (rateLimit) {
              console.log(`速率限制: ${rateRemaining}/${rateLimit}`);
              console.log(`重置时间: ${new Date(rateReset * 1000)}`);
            }
            
            // 处理自定义业务头
            const apiVersion = this.getResponseHeader("X-API-Version");
            const requestId = this.getResponseHeader("X-Request-ID");
            
            processResponse({
              data: this.response,
              metadata: {
                contentType: this.getResponseHeader("Content-Type"),
                contentLength: this.getResponseHeader("Content-Length"),
                apiVersion: apiVersion,
                requestId: requestId
              }
            });
          }
        }
      };
      
      xhr.onerror = function() {
        console.error("请求失败");
      };
      
      xhr.send();
    }

事件

loadstart

loadstart(event)=>void首个事件,在浏览器开始加载服务器响应时触发。

  • eventProgressEvent,事件对象。包含以下属性:
    • lengthComputableboolean, 是否可计算总大小(通常为 false

    • loadednumber,已加载字节数(初始值通常为0)

    • totalnumber,总字节数(当 lengthComputable=true 时有效)

    • timeStampnumber,事件触发时间戳(毫秒)

基本示例

  1. 处理函数示例

    js
    xhr.addEventListener("loadstart", function(event) {
      console.log("请求开始:", event.timeStamp);
      console.log("是否可计算大小:", event.lengthComputable); // 通常为 false
      console.log("已加载:", event.loaded, "字节"); // 通常为 0
    });

核心特性

  1. 不可计算大小

    loadstart 事件中:

    js
    event.lengthComputable // 通常为 false
    event.loaded           // 通常为 0
    event.total           // 通常为 0

    因为此时服务器尚未返回 Content-Length 头信息

  2. 同步请求限制

    在同步请求中可能不会触发事件:

    js
    xhr.open("GET", "/data", false); // 同步
    xhr.send();
    // loadstart 可能不会触发
  3. 与 readystatechange 的关系

    触发顺序:

    • readystatechange (状态1: OPENED)
    • loadstart
    • readystatechange (状态2: HEADERS_RECEIVED)
    • 其他事件...
  4. 多次触发

    每个请求仅触发一次 loadstart,即使重定向:

    js
    // 重定向场景
    Request: ABC
    事件: loadstart (A) → redirect → loadstart (C) ❌
    实际: loadstart (A) → redirect → loadend (A) → loadstart (C) ✅

进阶示例

  1. 请求初始化

    js
    xhr.addEventListener("loadstart", () => {
      // 显示加载指示器
      document.getElementById("loader").style.display = "block";
      
      // 禁用提交按钮
      submitButton.disabled = true;
      
      // 初始化性能计时
      window.performance.mark("request-start");
    });
  2. 诊断请求延迟

    js
    let requestStartTime;
    
    xhr.addEventListener("loadstart", (e) => {
      requestStartTime = e.timeStamp;
    });
    
    xhr.addEventListener("load", (e) => {
      const totalTime = e.timeStamp - requestStartTime;
      console.log(`请求耗时: ${totalTime.toFixed(2)}ms`);
    });

load@

load(event)=>void核心成功事件,当请求成功完成且响应数据完全接收时触发。

  • eventProgressEvent,事件对象。包含以下属性:
    • lengthComputableboolean, 是否可计算总大小(通常为 true

    • loadednumber,已加载字节数(初始值通常为0)

    • totalnumber,总字节数(当 lengthComputable=true 时有效)

    • timeStampnumber,事件触发时间戳(毫秒)

基本示例

  1. 处理函数示例

    js
    xhr.addEventListener("load", function(event) {
      // 访问 XMLHttpRequest 实例
      const xhr = event.target;
      
      if (xhr.status >= 200 && xhr.status < 300) {
        console.log("成功响应:", xhr.response);
      } else {
        console.error(`HTTP错误: ${xhr.status}`);
      }
    });

核心特性

  1. 替代 readystatechange

    js
    // 传统方式
    xhr.onreadystatechange = function() {
      if (this.readyState === 4) {
        // 传统处理
      }
    };
    js
    //  现代方式
    xhr.onload = function() {
      // 现代处理(只关注成功状态)
    };

进阶示例

  1. API数据处理

    js
    xhr.addEventListener("load", function() {
      try {
        const data = JSON.parse(this.responseText);
        
        // 处理分页数据
        if (data.pagination) {
          renderItems(data.items);
          updatePagination(data.pagination);
        } 
        
        // 处理错误格式
        else if (data.error) {
          showUserError(data.error.message);
        }
      } catch (e) {
        console.error("JSON解析失败", e);
      }
    });
  2. 文件下载监控

    js
    xhr.responseType = "blob";
    
    xhr.addEventListener("load", function() {
      if (this.status === 200) {
        const blob = this.response;
        const url = URL.createObjectURL(blob);
        
        // 创建下载链接
        const a = document.createElement("a");
        a.href = url;
        a.download = "report.pdf";
        document.body.appendChild(a);
        a.click();
        
        // 清理
        setTimeout(() => {
          URL.revokeObjectURL(url);
          document.body.removeChild(a);
        }, 100);
      }
    });
  3. 请求性能分析

    js
    const perfMetrics = {};
    
    xhr.addEventListener("loadstart", () => {
      perfMetrics.start = performance.now();
    });
    
    xhr.addEventListener("load", (e) => {
      perfMetrics.end = performance.now();
      perfMetrics.duration = perfMetrics.end - perfMetrics.start;
      
      // 获取资源计时数据
      const resourceEntry = performance.getEntriesByName(e.target.responseURL)[0];
      
      console.table({
        "总耗时 (ms)": perfMetrics.duration.toFixed(2),
        "DNS查询 (ms)": (resourceEntry.domainLookupEnd - resourceEntry.domainLookupStart).toFixed(2),
        "TCP连接 (ms)": (resourceEntry.connectEnd - resourceEntry.connectStart).toFixed(2),
        "TTFB (ms)": (resourceEntry.responseStart - resourceEntry.requestStart).toFixed(2),
        "下载时间 (ms)": (resourceEntry.responseEnd - resourceEntry.responseStart).toFixed(2)
      });
    });

loadend

loadend(event)=>void最终事件,在请求结束时触发(无论成功或失败)。用于清理资源执行最终操作

  • eventProgressEvent,事件对象。包含以下属性:
    • lengthComputableboolean, 是否可计算总大小(取决于服务器响应)

    • loadednumber,最终加载字节数(成功时为总字节数,失败时为已加载字节)

    • totalnumber,总字节数(当 lengthComputable=true 时有效)

    • timeStampnumber,事件触发时间戳(毫秒)

基本示例

  1. 处理函数示例

    js
    xhr.addEventListener("loadend", function(event) {
      // 隐藏加载指示器
      document.getElementById("loader").style.display = "none";
      
      // 启用提交按钮
      submitButton.disabled = false;
      
      // 记录请求完成时间
      console.log(`请求结束于: ${new Date(event.timeStamp).toLocaleTimeString()}`);
    });

核心特性

  1. loadend 始终会触发

    即使以下情况依然会触发 loadend:

    • 网络断开(触发 error + loadend)
    • 请求取消(触发 abort + loadend)
    • 页面关闭(大多数浏览器仍会触发)

进阶示例

  1. 资源清理

    js
    let requestInProgress = false;
    
    xhr.addEventListener("loadstart", () => {
      requestInProgress = true;
      console.log("请求开始");
    });
    
    xhr.addEventListener("loadend", () => {
      requestInProgress = false;
      console.log("请求结束,清理临时资源");
      
      // 清理操作
      URL.revokeObjectURL(uploadedFileUrl);
      temporaryBuffer = null;
    });
  2. UI状态恢复

    js
    // 请求开始时
    xhr.addEventListener("loadstart", () => {
      submitButton.textContent = "处理中...";
      submitButton.disabled = true;
      errorMessage.style.display = "none";
    });
    
    // 无论结果如何,最终恢复UI
    xhr.addEventListener("loadend", () => {
      submitButton.textContent = "提交";
      submitButton.disabled = false;
      
      // 隐藏加载指示器
      document.getElementById("spinner").style.display = "none";
    });
  3. 请求性能分析

    js
    const perf = {
      start: 0,
      end: 0
    };
    
    xhr.addEventListener("loadstart", (e) => {
      perf.start = e.timeStamp;
    });
    
    xhr.addEventListener("loadend", (e) => {
      perf.end = e.timeStamp;
      const duration = perf.end - perf.start;
      
      // 发送性能数据到监控系统
      sendAnalytics({
        type: "xhr_performance",
        url: xhr.responseURL,
        duration: duration,
        status: xhr.status,
        loaded: e.loaded,
        total: e.total
      });
      
      console.log(`请求总耗时: ${duration.toFixed(2)}ms`);
    });

progress@

progress

  • :``,

  • :``,

  • :``,

  • 返回:

  • :``,

基本示例

  1. ****:

    js

核心特性

  1. ****:

    js
  2. ****:

    js
  3. ****:

    js

进阶示例

  1. ****:

    js
  2. ****:

    js
  3. ****:

    js

abort【

abort

  • :``,

  • :``,

  • :``,

  • 返回:

  • :``,

基本示例

  1. ****:

    js

核心特性

  1. ****:

    js
  2. ****:

    js
  3. ****:

    js

进阶示例

  1. ****:

    js
  2. ****:

    js
  3. ****:

    js

error@【

error

  • :``,

  • :``,

  • :``,

  • 返回:

  • :``,

基本示例

  1. ****:

    js

核心特性

  1. ****:

    js
  2. ****:

    js
  3. ****:

    js

进阶示例

  1. ****:

    js
  2. ****:

    js
  3. ****:

    js

timeout【

timeout

  • :``,

  • :``,

  • :``,

  • 返回:

  • :``,

基本示例

  1. ****:

    js

核心特性

  1. ****:

    js
  2. ****:

    js
  3. ****:

    js

进阶示例

  1. ****:

    js
  2. ****:

    js
  3. ****:

    js