前端网络与存储进阶全解|XHR进阶、Fetch流式请求响应、WebSocket、本地存储监听限制、IndexedDB

网络通信和本地存储真正拉开差距的地方,不是会不会发请求,而是能不能处理流式数据、长连接、离线缓存和浏览器存储限制这些真实工程问题。

网络通信是前端与服务端数据交互的核心,本地存储是前端离线、缓存、状态持久化的基石。初级开发者只会简单调用接口,中高级开发者需要掌握底层通信原理、流式传输、双向实时通信、存储限制与容错、大数据离线数据库

本文全覆盖指定核心知识点: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 生成)

本文总结

  • XHR 仍然在上传进度、超时控制和请求中断这些场景里有实际价值,而 Fetch 的真正进阶能力在于流式读取和更现代的异步模型。
  • WebSocket 解决的是双向实时通信问题,本地存储和 IndexedDB 解决的是离线持久化与大数据缓存问题,它们不是同一层能力。
  • 理解存储监听限制、长连接心跳重连和离线数据库事务模型,才算真正掌握了前端网络与存储的工程落地。
GYSTACK 文章文末广告 硅云云服务器活动 适合个人项目、轻量建站和出海业务部署。
后浪云移动端信息流广告 后浪云主机服务 适合长期部署、独立站和海外机房需求。