网络通信是前端与服务端数据交互的核心,本地存储是前端离线、缓存、状态持久化的基石。初级开发者只会简单调用接口,中高级开发者需要掌握底层通信原理、流式传输、双向实时通信、存储限制与容错、大数据离线数据库。
本文全覆盖指定核心知识点:XMLHttpRequest 进阶特性、Fetch 流式请求与响应、WebSocket 实时双向通信、localStorage/sessionStorage 监听机制与容量限制、IndexedDB 离线数据库,适配工程开发、性能优化、面试高频考点、离线业务落地。
一、XMLHttpRequest 进阶全解(经典网络API)
XHR 是前端最古老的异步网络API,虽被 Fetch 替代,但大量遗留项目、上传进度、中断请求、超时控制仍依赖它,也是面试高频基础考点,需掌握进阶特性。
1.1 XHR 核心进阶能力
-
精准超时控制:timeout 属性,支持毫秒级超时中断
-
实时进度监听:upload/download 进度事件,用于文件上传/下载进度展示
-
请求中断:abort() 终止请求,杜绝无效请求占用带宽
-
表单二进制上传:搭配 FormData 无缝实现文件、表单数据上传
-
同步/异步双模式:支持阻塞式同步请求(不推荐生产使用)
1.2 进阶实战:带进度、超时、可中断的请求
function createXHRRequest(url, options = {}) {
const { method = 'GET', timeout = 5000, data = null } = options
const xhr = new XMLHttpRequest()
// 初始化请求
xhr.open(method, url, true)
xhr.timeout = timeout
// 下载进度监听
xhr.onprogress = (e) => {
if (e.lengthComputable) {
const percent = (e.loaded / e.total * 100).toFixed(2)
console.log('下载进度:', percent + '%')
}
}
// 上传进度监听
xhr.upload.onprogress = (e) => {
if (e.lengthComputable) {
const percent = (e.loaded / e.total * 100).toFixed(2)
console.log('上传进度:', percent + '%')
}
}
// 超时回调
xhr.ontimeout = () => console.log('请求超时')
// 请求完成
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
console.log('请求成功', JSON.parse(xhr.responseText))
}
}
// 错误监听
xhr.onerror = () => console.log('网络请求异常')
// 发送请求
xhr.send(data)
// 返回终止方法
return {
abort: () => xhr.abort()
}
}
// 使用:可随时中断请求
const req = createXHRRequest('https://jsonplaceholder.typicode.com/todos/1')
// req.abort() // 手动终止请求
1.3 XHR 核心缺陷(被Fetch替代原因)
-
回调地狱范式,代码嵌套冗余,不支持 Promise
-
API 设计老旧,参数繁琐,配置不直观
-
无原生流式读取,大数据体验差
-
不支持模块化、异步语法糖适配
二、Fetch 进阶|流式请求与流式响应(高阶核心)
Fetch 是现代浏览器标准网络API,基于 Promise 设计,摆脱回调地狱。其流式传输(Stream)是最大进阶亮点,专门用于大文件下载、AI流式输出、实时分块响应,是当前工程高频刚需能力。
2.1 Fetch 基础特性与优缺点
-
基于 Promise,支持 async/await,代码简洁优雅
-
原生支持 ReadableStream 流式数据
-
天生支持 CORS、请求头、请求体精细化配置
-
经典坑点:Fetch 只报错网络异常,404/500 接口错误不会 reject,需手动判断状态码
-
无原生超时、进度监听(需手动封装)
2.2 核心进阶:Fetch 流式响应(逐块读取数据)
普通请求需等待全部数据返回再渲染,流式响应可边接收、边解析、边渲染,极致优化首屏速度、适配AI对话、大文本、大文件场景。核心依托 response\.body 可读流。
// 模拟流式接口请求(AI问答/大文本场景)
async function fetchStreamData(url) {
const response = await fetch(url)
// 校验响应状态
if (!response.ok) throw new Error('请求失败')
// 获取可读流
const reader = response.body.getReader()
// 文本解码器
const decoder = new TextDecoder('utf-8')
let result = ''
// 循环逐块读取流数据
while (true) {
const { done, value } = await reader.read()
// 读取完毕终止循环
if (done) break
// 解码二进制数据,拼接内容
const chunk = decoder.decode(value, { stream: true })
result += chunk
// 实时渲染片段,实现流式输出
console.log('实时分片数据:', chunk)
}
console.log('完整数据:', result)
}
2.3 Fetch 流式请求(上传流)
支持将本地文件流、分片数据作为请求体上传,实现大文件分片流式上传,减少内存占用。
2.4 Fetch 超时与中断封装
async function fetchWithTimeout(url, timeout = 3000) {
// 超时中断控制器
const controller = new AbortController()
const timer = setTimeout(() => controller.abort(), timeout)
try {
const res = await fetch(url, { signal: controller.signal })
clearTimeout(timer)
return res
} catch (err) {
if (err.name === 'AbortError') {
console.log('请求超时/手动中断')
}
}
}
三、WebSocket 实时双向通信(实时业务核心)
HTTP 是单向短连接,每次请求响应后断开;WebSocket 是双向长连接,一次握手、永久连接、服务端可主动推送数据,是聊天、直播、弹幕、实时监控、在线协同的核心方案。
3.1 WebSocket 核心特性
-
基于 TCP 协议,全双工双向通信,客户端、服务端均可主动发数据
-
一次 HTTP 握手,后续永久长连接,无需重复建立连接
-
数据开销极小,头部体积远小于 HTTP,实时性极高
-
支持文本、二进制数据传输
-
默认支持跨域,无需额外配置CORS
3.2 原生 WebSocket 完整实战(心跳+重连)
原生WS默认无心跳、无重连,生产环境必须封装心跳检测+断连重连,解决网络波动、服务端断开问题。
class MyWebSocket {
constructor(url) {
this.url = url
this.ws = null
this.heartTimer = null
this.reconnectTimer = null
this.heartTime = 3000 // 心跳间隔
this.reconnectTime = 5000 // 重连间隔
this.init()
}
// 初始化连接
init() {
this.ws = new WebSocket(this.url)
this.onOpen()
this.onMessage()
this.onClose()
this.onError()
}
// 连接成功
onOpen() {
this.ws.onopen = () => {
console.log('WS连接成功')
this.startHeart() // 开启心跳
}
}
// 接收服务端消息
onMessage() {
this.ws.onmessage = (e) => {
console.log('接收消息:', e.data)
}
}
// 连接关闭
onClose() {
this.ws.onclose = () => {
console.log('WS连接断开')
this.clearTimer()
this.reconnect() // 自动重连
}
}
// 连接报错
onError() {
this.ws.onerror = (err) => {
console.error('WS连接异常', err)
this.ws.close()
}
}
// 开启心跳检测
startHeart() {
this.heartTimer = setInterval(() => {
this.ws.readyState === WebSocket.OPEN && this.ws.send('heart')
}, this.heartTime)
}
// 自动重连
reconnect() {
this.reconnectTimer = setTimeout(() => {
this.init()
}, this.reconnectTime)
}
// 清空定时器
clearTimer() {
clearInterval(this.heartTimer)
clearTimeout(this.reconnectTimer)
}
// 发送消息
send(data) {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(data)
}
}
}
3.3 HTTP vs WebSocket 核心对比
| 特性 | HTTP | WebSocket |
|---|---|---|
| 连接模式 | 短连接,请求后立即断开 | 长连接,一次握手永久连接 |
| 通信方向 | 客户端主动请求,服务端被动响应(单向) | 客户端/服务端双向主动通信(全双工) |
| 开销 | 请求头大,频繁请求开销高 | 数据包极小,开销极低 |
| 适用场景 | 普通接口查询、静态资源请求 | 实时聊天、弹幕、监控、协同编辑 |
四、localStorage / sessionStorage 进阶(监听、限制、坑点)
本地存储是前端轻量持久化方案,二者统称 WebStorage,多用于缓存简单数据、登录状态、配置项,面试高频考察容量限制、生命周期、监听机制、跨页面通信。
4.1 核心特性与差异
| 特性 | localStorage | sessionStorage |
|---|---|---|
| 生命周期 | 永久存储,手动清除才失效 | 会话级存储,关闭标签页立即清空 |
| 共享范围 | 同域名所有页面/标签共享 | 仅当前标签页独享,跨标签不共享 |
| 存储容量 | 约 5MB 同源限制 | 约 5MB 同源限制 |
| 存储类型 | 仅支持字符串,对象需JSON序列化 | 仅支持字符串 |
4.2 存储核心限制(工程必知)
-
容量限制:单域名最大 5MB,超出抛出异常,无法扩容
-
类型限制:只能存字符串,数字、对象、数组必须序列化存储
-
同步阻塞:读写为同步操作,大数据读写会阻塞主线程
-
域名隔离:严格同源策略,跨域名无法读取
-
隐私模式限制:部分浏览器隐私模式下,Storage 仅会话有效,关闭即清空
4.3 Storage 监听机制(跨页面通信核心)
原生提供 storage 事件,实现跨标签页监听存储变更,是简易跨页面通信方案。
核心特性:当前页面修改Storage不会触发自身监听,仅其他同源标签页触发
// 监听本地存储变更(其他页面修改触发)
window.addEventListener('storage', (e) => {
console.log('存储变更')
console.log('变更key:', e.key)
console.log('旧值:', e.oldValue)
console.log('新值:', e.newValue)
})
// 测试修改
localStorage.setItem('token', '123456')
4.4 工程坑点与解决方案
-
存储对象丢失格式:必须 JSON.stringify / JSON.parse 序列化反序列化
-
重复赋值不触发监听:仅新旧值不同才触发 storage 事件
-
容量溢出报错:写入前需判断容量,捕获异常兜底
五、IndexedDB 前端离线数据库(大数据存储终极方案)
WebStorage 仅适合小数据缓存,IndexedDB 是浏览器原生非关系型离线数据库,用于存储海量结构化数据、文件、二进制数据,是离线应用、PWA、大数据缓存的核心技术。
5.1 IndexedDB 核心优势
-
超大存储容量:默认几十MB,可申请GB级扩容,远超5MB Storage
-
异步操作:所有读写异步执行,不阻塞主线程
-
结构化存储:支持对象、数组、二进制、文件,无需序列化
-
事务支持:增删改查基于事务,保证数据一致性
-
索引查询:支持建立索引,精准查询、筛选数据
-
永久存储:除非手动清空,否则永久持久化
5.2 核心概念梳理
-
数据库(DB):一个域名可创建多个独立数据库
-
对象仓库(ObjectStore):相当于数据表,存储结构化对象数据
-
索引(Index):数据表字段索引,用于快速查询筛选
-
事务(Transaction):所有操作必须在事务中执行,支持读写/只读模式
5.3 IndexedDB 增删改查完整实战
// 打开/创建数据库
function openDB() {
return new Promise((resolve, reject) => {
const req = indexedDB.open('UserDB', 1)
// 数据库升级/首次创建触发
req.onupgradeneeded = (e) => {
const db = e.target.result
// 创建对象仓库(数据表),主键id
if (!db.objectStoreNames.contains('user')) {
const store = db.createObjectStore('user', { keyPath: 'id' })
// 创建索引,可查询name/age
store.createIndex('name', 'name', { unique: false })
store.createIndex('age', 'age', { unique: false })
}
}
req.onsuccess = () => resolve(req.result)
req.onerror = () => reject(req.error)
})
}
// 新增数据
async function addUser(user) {
const db = await openDB()
const store = db.transaction('user', 'readwrite').objectStore('user')
store.add(user)
}
// 查询数据
async function getUser(id) {
const db = await openDB()
const store = db.transaction('user').objectStore('user')
return new Promise(resolve => store.get(id).onsuccess = e => resolve(e.target.result))
}
// 更新数据
async function updateUser(user) {
const db = await openDB()
const store = db.transaction('user', 'readwrite').objectStore('user')
store.put(user)
}
// 删除数据
async function delUser(id) {
const db = await openDB()
const store = db.transaction('user', 'readwrite').objectStore('user')
store.delete(id)
}
// 测试
// addUser({id:1, name:'张三', age:20})
5.4 前端存储方案选型准则
-
简单小数据、配置、Token:优先 localStorage
-
临时会话数据、页面临时状态:优先 sessionStorage
-
海量数据、离线缓存、文件存储、结构化查询:必须使用 IndexedDB
六、网络API 全方位选型对比
| API | 优势场景 | 劣势 |
|---|---|---|
| XHR | 文件上传进度、兼容低端浏览器、精细请求控制 | 回调嵌套、API老旧、不支持流式优雅处理 |
| Fetch | Promise语法、流式传输、AI输出、大文件下载、代码简洁 | 无原生进度、超时、错误拦截需手动封装 |
| WebSocket | 实时双向通信、长连接、低开销、服务端主动推送 | 需手动封装心跳重连,短请求场景浪费资源 |
七、全文核心总结(面试必背)
-
XHR进阶:支持进度监听、超时、中断、表单上传,老旧回调模式,适配兼容与上传场景
-
Fetch流式:基于可读流实现分块读写,支持AI流式输出、大文件下载,是现代异步网络首选
-
WebSocket:HTTP握手双向长连接,实时通信专用,需封装心跳重连保证稳定性
-
WebStorage:5MB容量限制、仅存字符串、localStorage永久持久、sessionStorage会话失效,storage事件实现跨页面监听
-
IndexedDB:前端海量离线数据库,异步事务、结构化存储、大容量,适配离线PWA、大数据缓存场景
八、高频面试简答题
-
Fetch和XHR的区别? XHR为回调模式,支持精细进度、超时控制;Fetch基于Promise,语法简洁,原生支持流式传输,无原生错误与超时处理,404/500不触发reject。
-
什么是流式响应?应用场景? 基于ReadableStream逐块接收渲染数据,无需等待全部响应完成,用于AI对话流式输出、大文件下载、超大文本渲染,大幅提升体验。
-
WebSocket为什么需要心跳重连? 网络波动、防火墙会静默断开长连接,心跳检测保活,断连自动重连保证实时通信稳定性。
-
localStorage容量限制与坑点? 单域名5MB、仅字符串存储、同步阻塞、跨页面可监听,当前页面修改不触发自身storage事件。
-
IndexedDB相比Storage的优势? 容量超大、异步不阻塞、支持结构化数据与索引查询、事务操作,可存储海量数据与文件,适配离线业务。
-
实时业务为什么不用轮询而用WebSocket? 轮询频繁HTTP请求开销大、延迟高;WebSocket一次握手永久长连接、双向推送、开销极低、实时性更强。
(注:文档部分内容可能由 AI 生成)