网络 API
XMLHttpRequest 和 Fetch API 的区别 ?
答案
维度 | XMLHttpRequest (XHR) | Fetch API |
---|---|---|
API风格 | 基于回调函数,事件驱动 | 基于Promise,链式调用 |
语法简洁性 | 语法繁琐,需多步配置 | 语法简洁,易于维护 |
请求/响应处理 | 需手动解析JSON、处理状态 | json()等方法自动解析 |
请求头设置 | setRequestHeader方法 | Headers对象,配置灵活 |
请求体 | 字符串、FormData等 | 支持FormData、Blob、URLSearchParams等 |
取消请求 | 通过abort()方法 | 支持AbortController/AbortSignal |
进度事件 | 支持upload/download进度事件 | 仅部分支持,需额外处理 |
跨域 | 支持CORS,配置复杂 | 支持CORS,配置直观 |
错误处理 | 需判断readyState/status | 仅网络错误/中止抛异常,状态码需手动判断 |
文件上传/下载 | 支持 | 支持 |
浏览器兼容性 | 兼容性好,老浏览器支持 | 新标准,低版本需polyfill |
自定义请求 | 支持 | 支持Request对象,功能丰富 |
补充说明
- XHR 适合兼容性要求高、需进度事件的场景,但API繁琐。
- Fetch API 语法现代,Promise链式处理更优雅,推荐新项目优先使用。
- Fetch 支持通过 AbortController 取消请求,XHR 通过 abort() 方法实现。
- 错误处理上,Fetch 只对网络错误抛异常,状态码错误需手动判断。
// XHR 示例
const xhr = new XMLHttpRequest()
xhr.open('GET', '/api/data')
xhr.onload = () => { if (xhr.status === 200) console.log(xhr.responseText) }
xhr.send()
// Fetch 示例
fetch('/api/data')
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err))
推荐新项目优先使用 Fetch API,语法更现代,易于维护。需兼容老浏览器时可用 XHR 或引入 polyfill。
延伸阅读
Long-Polling、Websockets 和 Server-Sent Event 之间有什么区别?
答案
维度 | Long-Polling | WebSocket | Server-Sent Event (SSE) |
---|---|---|---|
连接方式 | HTTP短连接 | TCP长连接 | HTTP长连接 |
数据流向 | 单向(服务端→客户端) | 双向(全双工) | 单向(服务端→客户端) |
实时性 | 较好 | 极佳 | 好 |
兼容性 | 广泛 | 现代浏览器 | 较好(IE不兼容) |
典型场景 | 消息推送、IM | 实时协作、游戏、IM | 实时通知、数据流 |
特点/限制 | 每次响应后需重新请求,资源消耗大 | 需服务端支持,初始握手复杂 | 仅支持文本,无法双向通信 |
补充说明
- Long-Polling 通过不断发起 HTTP 请求模拟实时推送,兼容性好但效率低。
- WebSocket 支持双向通信,适合高实时性场景,但需服务端专门支持。
- SSE 适合服务端单向推送,API简单,自动重连,但不支持 IE,且仅支持文本数据。
如需双向实时通信优先选 WebSocket,单向推送可用 SSE,兼容性优先时可选 Long-Polling。
延伸阅读
说下 navigator.sendBeacon
答案
特性 | sendBeacon | fetch(keepalive) |
---|---|---|
用途 | 异步发送小量数据,常用于埋点、统计 | 可发送更复杂请求,支持 keepalive 保证卸载时发送 |
触发时机 | 页面卸载、关闭等 | 页面卸载、关闭等 |
数据类型 | 文本、Blob、FormData 等 | 任意 body 类型 |
数据量限制 | 较小(通常几十 KB) | 约 64KB(各浏览器略有差异) |
返回值 | 布尔值,表示是否入队 | Promise,无法保证一定送达 |
阻塞卸载 | 否 | 否 |
兼容性 | 现代浏览器广泛支持 | 现代浏览器支持,部分低版本不支持 keepalive |
核心概念与原理
navigator.sendBeacon(url, data)
设计用于在页面卸载时异步、可靠地发送小量数据,不阻塞页面关闭。底层通过浏览器任务队列保证请求尽量送达,适合统计、日志等场景。fetch
的keepalive: true
选项也可用于类似场景,但有更严格的数据量限制,且部分老浏览器不支持。
常见误区
- 误以为 sendBeacon 能保证 100% 送达,实际上网络异常等仍可能丢失。
- sendBeacon 只适合小数据量,超出限制可能被丢弃。
- fetch keepalive 不是所有浏览器都支持,且请求体过大时会失败。
代码示例
// 推荐:页面卸载时发送埋点
window.addEventListener('unload', () => {
const data = { event: 'leave', time: Date.now() }
navigator.sendBeacon('/api/track', JSON.stringify(data))
})
// 备选方案:fetch + keepalive
window.addEventListener('beforeunload', () => {
const data = { event: 'leave', time: Date.now() }
fetch('/api/track', {
method: 'POST',
body: JSON.stringify(data),
headers: { 'Content-Type': 'application/json' },
keepalive: true
})
})
统计/埋点等需保证卸载时送达的小数据,优先用 sendBeacon。需发送大数据或自定义请求时可用 fetch+keepalive,但注意兼容性和体积限制。
延伸阅读
SSE
一个网页获取新的数据通常需要发送一个请求到服务器,也就是向服务器请求的页面。使用 server-sent 事件,服务器可以在任何时刻向我们的 Web 页面推送数据和信息。这些被推送进来的信息可以在这个页面上作为 Events + data 的形式来处理。
从服务器接受事件
服务器发送事件 API 也就是 EventSource 接口,在你创建一个新的 EventSource 对象的同时,你可以指定一个接受事件的 URI。例如:
const evtSource = new EventSource('ssedemo.php')
如果发送事件的脚本不同源,应该创建一个新的包含 URL 和 options 参数的EventSource对象。例如,假设客户端脚本在 example.com 上:
const evtSource = new EventSource('//api.example.com/ssedemo.php', { withCredentials: true })
一旦你成功初始化了一个事件源,就可以对 message 事件添加一个处理函数开始监听从服务器发出的消息了:
evtSource.onmessage = function (event) {
const newElement = document.createElement('li')
const eventList = document.getElementById('list')
newElement.innerHTML = 'message: ' + event.data
eventList.appendChild(newElement)
}
你也可以使用addEventListener()方法来监听其他类型的事件:
evtSource.addEventListener('ping', function (event) {
const newElement = document.createElement('li')
const time = JSON.parse(event.data).time
newElement.innerHTML = 'ping at ' + time
eventList.appendChild(newElement)
})
警告: 当不通过 HTTP / 2 使用时,SSE(server-sent events)会受到最大连接数的限制,这在打开各种选项卡时特别麻烦,因为该限制是针对每个浏览器的,并且被设置为一个非常低的数字(6)。该问题在 Chrome 和 Firefox中被标记为“无法解决”。此限制是针对每个浏览器 + 域的,因此这意味着您可以跨所有选项卡打开 6 个 SSE 连接到
<www.example1.com,并打开>
6 个 SSE 连接到www.example2.com。(来自 Stackoverflow)1
。使用 HTTP / 2 时,HTTP 同一时间内的最大连接数由服务器和客户端之间协商(默认为 100)。
SSE 的 API
属性(只读)
名称 | 作用 | 类型 | 备注 |
---|---|---|---|
readyState | 当前状态 | Number | 0 — connecting1 — open2 — closed |
url | 当前连接的地址 | String | |
withCredentials | 是否开启凭据收集 | Boolean |
方法
名称 | 作用 | 返回值 |
---|---|---|
close | 客户端主动关闭连接 | - |
事件
名称 | 作用 | 返回值 |
---|---|---|
onclose | 连接关闭触发 | event |
onopen | 连接开启触发 | event |
onmessage | 服务端消息推动消息触发 | event |
服务端 API
名称 | 作用 | 类型 | 备注 |
---|---|---|---|
data | 传输的文本 | String(默认). 可以传输JSON | 可以多行累加 |
event | 事件名称 | String | 可自定义 |
id | 当前推送 id | String | 作为消息的标识 |
retry | 超时重试时间 | Number | 客户端在感知 server 连接异常后。会通过 retry 设定时间进行重新连接 |
补充: SSE 与 WS 有什么区别?
方式 | 协议 | 交互通道 | 内容编码 | 重连 | 事件类型 | 总结 |
---|---|---|---|---|---|---|
SSE | HTTP | 服务端单向推送 | 默认文本 | 默认支持断线重连 | 支持自定义消息类型 | 轻量级 |
WebSocket | WS(基于 TCP 传输层的应用层协议,RFC6455 [1] 对于它的定义标准) | 双向推送 | 默认二进制 | 手动实现 | NO | 扩展性、功能性强大 |
参考文档