XMLHttpRequest
[TOC]
索引
构造方法:
- new XMLHttpRequest():
()
,用于创建一个 XHR 实例对象。用于与服务器交互,支持异步 HTTP 请求(如 AJAX)。
属性:
- xhr.onreadystatechange:
fn
,事件处理,用于监听请求状态变化。会在readyState
属性值变化时触发,是处理异步请求响应的传统方式。 - xhr.readyState:
0|1|2|3|4
,只读,表示请求的当前状态。 - xhr.status:
number
,只读,表示 HTTP 响应的状态码。 - xhr.statusText:
string
,只读,用于获取 HTTP 响应的状态文本。 - xhr.response:
any
,只读,用于获取 HTTP 响应的内容主体。可以根据responseType
设置自动解析响应内容。 - xhr.responseType:
string
,默认:'text'
,可读写,用于指定服务器响应的数据类型,并控制如何解析响应数据。 - xhr.timeout:
number
,默认:0
,可读写,用于设置 HTTP 请求的超时时间(毫秒)。超时后请求自动终止并触发timeout
事件。 - xhr.withCredentials:
boolean
,默认: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)。
返回:
xhr:
XMLHttpRequest
,返回一个 XHR 对象实例。用于发起 HTTP 请求,包含请求配置、发送和响应处理的方法与属性。
基本示例:
完整工作流程:
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();
注意事项:
跨域请求 (CORS):
需服务器设置响应头(如
Access-Control-Allow-Origin
),否则浏览器会阻止响应。同步请求已弃用:
设置
async: false
会导致页面阻塞(不推荐)。响应类型:
设置
xhr.responseType = "json"
可自动解析 JSON 响应(通过xhr.response
访问)。安全性:
避免向不可信来源发送敏感数据。
替代方案:
- fetch:更现代、基于 Promise 的替代方案(推荐新项目使用)。
- Axios:基于 Promise 的第三方 HTTP 库(支持浏览器和 Node.js)。
属性
onreadystatechange@
xhr.onreadystatechange:fn
,事件处理,用于监听请求状态变化。会在 readyState
属性值变化时触发,是处理异步请求响应的传统方式。
- fn:
()=>void
,事件处理函数。无显式参数,但可通过this
或闭包访问 XHR 对象。
基本示例:
基础示例:
jsconst 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();
核心特性:
触发时机:
每当
readyState
值改变时触发(包括所有状态变化):对比 替代事件:
事件 触发时机 推荐度 onreadystatechange
所有 readyState 变化 △ 传统 onload
请求成功完成 (readyState=4) ★★★ onerror
请求失败时 ★★★ onprogress
数据传输过程中周期性触发 ★★☆ jsxhr.onload = function() { if (this.status === 200) { console.log(this.response); } }; xhr.onerror = function() { console.error("网络错误"); };
进阶示例:
结合进度事件:
jsxhr.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}%`); } };
事件移除:赋值为
null
可移除事件监听js// 移除事件监听 xhr.onreadystatechange = null; // 一次性事件 xhr.onreadystatechange = function handler() { if (this.readyState === 4) { // 执行操作... xhr.onreadystatechange = null; // 移除自身 } };
readyState@
xhr.readyState:0|1|2|3|4
,只读,表示请求的当前状态。
核心特性:
状态机详解:
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
1: OPENED
(已打开)触发条件:成功调用
open()
方法后可用操作:
- 设置请求头
setRequestHeader()
- 配置超时
timeout
- 设置凭证
withCredentials
jsxhr.open("GET", "/api/data", true); console.log(xhr.readyState); // 1
- 设置请求头
2: HEADERS_RECEIVED
(头信息已接收)触发条件:接收到服务器响应头
可用属性:
status
: HTTP 状态码statusText
: 状态文本getResponseHeader()
getAllResponseHeaders()
jsxhr.onreadystatechange = function() { if (this.readyState === 2) { console.log("状态码:", this.status); console.log("Content-Type:", this.getResponseHeader("Content-Type")); } };
3: LOADING
(数据加载中)触发条件:开始接收响应体
关键特性:
- 周期性触发(可能多次)
- 可访问部分接收的数据
可用属性:
responseText
(部分数据)response
(部分数据)
jsxhr.onreadystatechange = function() { if (this.readyState === 3) { console.log("已接收部分数据:", this.responseText.slice(0, 50)); } };
4: DONE
(请求完成)触发条件:请求操作完成(无论成功或失败)
关键检查:
status
: HTTP 状态码statusText
: 状态描述
可用属性:
responseText
: 完整响应文本response
: 根据 responseType 解析的响应responseXML
: XML 响应
jsxhr.onreadystatechange = function() { if (this.readyState === 4) { if (this.status === 200) { console.log("完整响应:", this.response); } else { console.error("请求失败:", this.status); } } };
同步请求的状态变化:同步请求中状态变化在 send() 后立即完成
js// 同步请求中状态变化在 send() 后立即完成 xhr.open("GET", "/data", false); // 同步模式 console.log(xhr.readyState); // 1 (OPENED) xhr.send(); console.log(xhr.readyState); // 4 (DONE) 直接跳转
status@
xhr.status:number
,只读,表示 HTTP 响应的状态码。
基本示例:
基础状态检查:
jsxhr.onreadystatechange = function() { if (this.readyState === 4) { // 主成功范围 (200-299) if (this.status >= 200 && this.status < 300) { console.log("请求成功"); } else { console.error(`请求失败: ${this.status} ${this.statusText}`); } } };
核心特性:
值范围 类别 典型值示例 说明 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 错误 - 请求未发送或完全失败 特殊状态 0:
出现场景:
- 请求被取消 (
xhr.abort()
) - 网络连接断开
- 跨域请求被浏览器阻止
- 无效 URL 或协议错误
jsxhr.onerror = function() { if (this.status === 0) { console.error("请求失败:网络错误或请求被阻止"); } };
- 请求被取消 (
进阶示例:
完整工作流:
jsconst 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.statusText:string
,只读,用于获取 HTTP 响应的状态文本。
基本示例:
基础状态处理:
jsxhr.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); } } };
核心特性:
与 HTTP 状态码的对应关系:
xhr.statusText
与xhr.status
配合使用,提供完整的 HTTP 状态信息:jsxhr.onload = function() { console.log(`状态码: ${this.status}`); // 200 console.log(`状态文本: ${this.statusText}`); // "OK" if (this.status === 200) { console.log("请求成功:", this.statusText); // "请求成功: OK" } };
推荐基于状态码判断:基于状态文本判断可能不可靠
response@
xhr.response:any
,只读,用于获取 HTTP 响应的内容主体。可以根据 responseType
设置自动解析响应内容。
核心特性:
结合
responseType
:js// 1. 设置响应类型为 JSON xhr.responseType = "json"; xhr.onload = function() { // 2. 自动解析为 JS 对象 console.log(this.response.user.name); };
对比 其他响应属性:
属性 返回类型 特点 response
动态类型 根据 responseType
变化responseText
string
仅文本内容,忽略二进制 responseXML
Document
仅 XML/HTML 内容,需有效文档 responseURL
string
最终响应 URL (处理重定向后)
进阶示例:
文本响应处理:
jsxhr.responseType = "text"; xhr.onload = function() { console.log("文本内容:", this.response); };
JSON 数据处理:
jsxhr.responseType = "json"; xhr.onload = function() { if (this.status === 200) { console.log("用户:", this.response.name); } else { // 错误处理 console.error("错误数据:", this.response); } };
二进制数据处理:
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); };
XML 文档处理:
jsxhr.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.responseType:string
,默认:'text'
,可读写,用于指定服务器响应的数据类型,并控制如何解析响应数据。
基本示例:
自动数据解析:
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 对象 } };
核心特性:
responseType 接受值:
值 类型 说明 对应响应属性 ""
(空字符串)string
默认值,响应文本 responseText
"text"
string
响应文本 responseText
"json"
Object
自动解析的 JS 对象 response
"arraybuffer"
ArrayBuffer
二进制数据的原始缓冲区 response
"blob"
Blob
二进制大对象(文件等) response
"document"
Document
XML/HTML 文档对象 responseXML
进阶示例:
文本数据处理 (
""
或"text"
)jsxhr.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"); } } };
JSON 数据处理 (
"json"
)jsxhr.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 };
二进制数据处理 (
"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); } };
XML 文档处理 (
"document"
)jsxhr.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.timeout:number
,默认:0
,可读写,用于设置 HTTP 请求的超时时间(毫秒)。超时后请求自动终止并触发 timeout
事件。
基本示例:
基础设置:
jsconst 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();
核心特性:
- 默认值:
0
:表示无超时限制
withCredentials@
xhr.withCredentials:boolean
,默认:false
,可读写,用于控制跨域请求是否携带用户凭证(如 cookies、HTTP 认证信息)。
基本示例:
基础跨域认证:
jsconst 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();
核心特性:
凭证包含内容:
当设置为
true
时,请求会携带:- Cookies(包括会话 cookie)
- HTTP 认证头(Authorization)
- TLS 客户端证书
- Windows 域认证(IE 特有)
CORS 协作要求:
服务器头要求:
响应头 必需值 说明 Access-Control-Allow-Credentials
true
明确允许凭证 Access-Control-Allow-Origin
具体源 (非 *
)必须与请求源完全匹配 Access-Control-Expose-Headers
需暴露的自定义头 否则客户端无法访问 服务器 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天 });
- 不能设置
客户端响应访问限制:即使响应成功,也可能无法访问某些头(如
Set-Cookie
),需要服务器端暴露头jsxhr.withCredentials = true; xhr.onload = function() { // 即使成功,也可能无法访问某些头 console.log(this.getResponseHeader("Set-Cookie")); // null };
同源请求:自动携带凭证(忽略此设置)
进阶示例:
结合 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" }); });
预检请求处理:复杂请求触发预检 (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
完整工作流:
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()
方法使用。
method:
GET|POST|DELETE|PUT|PATCH|OPTIONS|HEAD
,HTTP 请求方法。url:
string
,请求的目标 URL(支持相对路径或绝对路径)。async?:
boolean
,默认:true
,是否异步执行(同步已废弃)。user?:
string
,默认:null
,HTTP 基础认证的用户名。password?:
string
,默认:null
,HTTP 基础认证的密码。
基本示例:
初始化请求:
jsconst xhr = new XMLHttpRequest(); xhr.open("POST", "/submit", true); // 异步 POST 请求
核心特性:
调用顺序:
- 必须在
.send()
前调用 - 必须在
.setRequestHeader()
前调用
- 必须在
跨域请求 (CORS):
需服务器设置
Access-Control-Allow-Origin
带认证的跨域请求需设置:
jsxhr.withCredentials = true; // 发送 cookies
异步 vs 同步:
同步请求已被废弃:会导致浏览器主线程阻塞,用户界面卡顿。
js// 异步请求(推荐) xhr.open("GET", "/data", true); // 同步请求(已废弃!阻塞页面执行) xhr.open("GET", "/data", false); // ⚠️ 避免使用
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}`);
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
或省略。
基本示例:
GET 请求(无请求体):
jsxhr.open("GET", "/api/data", true); xhr.send(); // 无参数
POST 请求(发送字符串):
jsxhr.open("POST", "/submit", true); xhr.setRequestHeader("Content-Type", "application/json"); xhr.send(JSON.stringify({ name: "John", age: 30 }));
POST 请求(发送 FormData 表单提交):
jsconst formData = new FormData(); formData.append("username", "john_doe"); formData.append("avatar", fileInput.files[0]); xhr.open("POST", "/upload", true); xhr.send(formData); // 自动设置 Content-Type
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);
POST 请求(发送 URL 编码数据):
jsconst params = new URLSearchParams(); params.append("key1", "value1"); params.append("key2", "value2"); xhr.open("POST", "/submit-form", true); xhr.send(params); // 自动设置 Content-Type
核心特性:
- 常见请求体类型:
null
或省略:无请求体(GET 请求默认)DOMString
:普通字符串(需手动设置Content-Type
)FormData
:表单数据(自动设置multipart/form-data
)Blob
:二进制大对象(如文件)ArrayBuffer
:原始二进制数据URLSearchParams
:查询字符串格式(自动设置application/x-www-form-urlencoded
)Document
:XML 文档
- 调用顺序:
send()
方法必须在最后调用
abort()
xhr.abort():()
,用于立即终止正在进行的 HTTP 请求。调用后,请求会被取消,不会继续接收响应数据。
基本示例:
完整示例:
jsconst 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(); } });
核心行为:
终止请求:
- 立即停止进行中的请求传输
- 停止接收服务器响应
- 将
readyState
重置为0
(UNSENT) - 设置
status
为0
触发事件:
- 触发
abort
事件 - 触发
readystatechange
事件(状态变为0
)
- 触发
后续处理:
- 可以安全地重用同一个 XMLHttpRequest 对象
- 需要重新调用
open()
和send()
发起新请求
进阶示例:
复用请求对象:终止后可以重新配置并发送新请求
js// 终止后可以重用 xhr.abort(); // 重新配置并发送新请求 xhr.open("GET", "/new-data", true); xhr.send();
setRequestHeader()@
xhr.setRequestHeader():(header, value)
,用于在发送请求前设置 HTTP 请求头。确保在 open()
之后、send()
之前调用。
header:
string
,HTTP 请求头的名称(不区分大小写)value:
string
,请求头的值
基本示例:
设置请求头:
js// 设置内容类型 xhr.setRequestHeader("Content-Type", "application/json"); // 设置认证令牌 xhr.setRequestHeader("Authorization", "Bearer token123"); // 设置自定义头 xhr.setRequestHeader("X-Custom-Header", "value");
核心特性:
受限制的请求头:
浏览器禁止设置以下安全相关的头(设置会被静默忽略):
Host
Connection
Keep-Alive
Accept-Encoding
Cookie
(使用xhr.withCredentials
代替)Content-Length
- 等(完整列表见 MDN 禁止的请求头)
自动设置的头:
浏览器会自动设置:
Content-Length
(根据请求体)User-Agent
Referer
Origin
(跨域请求时)
多次调用效果:
相同头名称的多次设置会合并值(逗号分隔)而非覆盖:
jsxhr.setRequestHeader("Accept", "application/json"); xhr.setRequestHeader("Accept", "text/xml"); // 实际发送:Accept: application/json, text/xml
需要覆盖值时需先移除原有头(无法直接覆盖)
自定义请求头:
自定义请求头会触发预检请求 (OPTIONS)
服务器需响应相应头:
httpAccess-Control-Allow-Headers: Content-Type, X-Custom-Header
携带 Cookie:
携带凭据需设置:
jsxhr.withCredentials = true;
进阶示例:
使用流程:
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 响应头的值。
headerName:
string
,要获取的 HTTP 响应头名称(不区分大小写)。返回:
header:
string|null
, 返回响应头的值(如果存在且可访问);响应头不存在、值为空或浏览器禁止访问返回 null。
基本示例:
基本用法:
jsxhr.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" } };
核心特性:
访问时机:在 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")); // 可用 } };
跨域限制 (CORS):
跨域请求时,默认只能访问简单响应头:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
若想访问,服务器需设置 Access-Control-Expose-Headers 来暴露其他头
httpAccess-Control-Expose-Headers: X-Custom-Header, X-Another-Header, Set-Cookie
jsxhr.onload = function() { // 默认不可访问 const customHeader = this.getResponseHeader("X-Custom-Header"); // null // 服务器暴露后可访问 const exposedHeader = this.getResponseHeader("X-Exposed-Header"); // "value" };
头名称大小写不敏感:
js// 头名称大小写不敏感 xhr.getResponseHeader("content-type") === xhr.getResponseHeader("Content-Type") === xhr.getResponseHeader("CONTENT-TYPE"); // true
进阶示例:
完整工作流:
jsfunction 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
,首个事件,在浏览器开始加载服务器响应时触发。
- event:
ProgressEvent
,事件对象。包含以下属性:lengthComputable:
boolean
, 是否可计算总大小(通常为false
)loaded:
number
,已加载字节数(初始值通常为0)total:
number
,总字节数(当lengthComputable=true
时有效)timeStamp:
number
,事件触发时间戳(毫秒)
基本示例:
处理函数示例:
jsxhr.addEventListener("loadstart", function(event) { console.log("请求开始:", event.timeStamp); console.log("是否可计算大小:", event.lengthComputable); // 通常为 false console.log("已加载:", event.loaded, "字节"); // 通常为 0 });
核心特性:
不可计算大小:
在
loadstart
事件中:jsevent.lengthComputable // 通常为 false event.loaded // 通常为 0 event.total // 通常为 0
因为此时服务器尚未返回
Content-Length
头信息同步请求限制:
在同步请求中可能不会触发事件:
jsxhr.open("GET", "/data", false); // 同步 xhr.send(); // loadstart 可能不会触发
与 readystatechange 的关系:
触发顺序:
readystatechange
(状态1: OPENED)loadstart
readystatechange
(状态2: HEADERS_RECEIVED)- 其他事件...
多次触发:
每个请求仅触发一次
loadstart
,即使重定向:js// 重定向场景 Request: A → B → C 事件: loadstart (A) → redirect → loadstart (C) ❌ 实际: loadstart (A) → redirect → loadend (A) → loadstart (C) ✅
进阶示例:
请求初始化:
jsxhr.addEventListener("loadstart", () => { // 显示加载指示器 document.getElementById("loader").style.display = "block"; // 禁用提交按钮 submitButton.disabled = true; // 初始化性能计时 window.performance.mark("request-start"); });
诊断请求延迟:
jslet 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
,核心成功事件,当请求成功完成且响应数据完全接收时触发。
- event:
ProgressEvent
,事件对象。包含以下属性:lengthComputable:
boolean
, 是否可计算总大小(通常为true
)loaded:
number
,已加载字节数(初始值通常为0)total:
number
,总字节数(当lengthComputable=true
时有效)timeStamp:
number
,事件触发时间戳(毫秒)
基本示例:
处理函数示例:
jsxhr.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}`); } });
核心特性:
替代
readystatechange
:js// 传统方式 xhr.onreadystatechange = function() { if (this.readyState === 4) { // 传统处理 } };
js// 现代方式 xhr.onload = function() { // 现代处理(只关注成功状态) };
进阶示例:
API数据处理:
jsxhr.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); } });
文件下载监控:
jsxhr.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); } });
请求性能分析:
jsconst 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
,最终事件,在请求结束时触发(无论成功或失败)。用于清理资源和执行最终操作。
- event:
ProgressEvent
,事件对象。包含以下属性:lengthComputable:
boolean
, 是否可计算总大小(取决于服务器响应)loaded:
number
,最终加载字节数(成功时为总字节数,失败时为已加载字节)total:
number
,总字节数(当lengthComputable=true
时有效)timeStamp:
number
,事件触发时间戳(毫秒)
基本示例:
处理函数示例:
jsxhr.addEventListener("loadend", function(event) { // 隐藏加载指示器 document.getElementById("loader").style.display = "none"; // 启用提交按钮 submitButton.disabled = false; // 记录请求完成时间 console.log(`请求结束于: ${new Date(event.timeStamp).toLocaleTimeString()}`); });
核心特性:
loadend 始终会触发:
即使以下情况依然会触发 loadend:
- 网络断开(触发 error + loadend)
- 请求取消(触发 abort + loadend)
- 页面关闭(大多数浏览器仍会触发)
进阶示例:
资源清理:
jslet requestInProgress = false; xhr.addEventListener("loadstart", () => { requestInProgress = true; console.log("请求开始"); }); xhr.addEventListener("loadend", () => { requestInProgress = false; console.log("请求结束,清理临时资源"); // 清理操作 URL.revokeObjectURL(uploadedFileUrl); temporaryBuffer = null; });
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"; });
请求性能分析:
jsconst 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:,
:``,
:``,
:``,
返回:
:``,
基本示例:
****:
js
核心特性:
****:
js****:
js****:
js
进阶示例:
****:
js****:
js****:
js
abort【
abort:,
:``,
:``,
:``,
返回:
:``,
基本示例:
****:
js
核心特性:
****:
js****:
js****:
js
进阶示例:
****:
js****:
js****:
js
error@【
error:,
:``,
:``,
:``,
返回:
:``,
基本示例:
****:
js
核心特性:
****:
js****:
js****:
js
进阶示例:
****:
js****:
js****:
js
timeout【
timeout:,
:``,
:``,
:``,
返回:
:``,
基本示例:
****:
js
核心特性:
****:
js****:
js****:
js
进阶示例:
****:
js****:
js****:
js