应分层抽象而非强行统一XHR、Fetch和WebSocket:按语义封装各自能力,共用拦截器、鉴权、错误处理等横切关注点,并通过语义化API(如api.user.getProfile())对外隐藏实现细节。

不需要强行统一成一个接口去调用 XHR、Fetch 和 WebSocket —— 它们本质不同:XHR/Fetch 是请求-响应模型,WebSocket 是双向长连接。硬塞进同一套“request”方法里,只会让逻辑混乱、错误难排查、调试成本高。合理的做法是分层抽象:按通信语义划分职责,各自封装,再通过统一的错误处理、拦截机制、状态管理来协同。
按场景分离网络能力,不强求“一个函数发所有请求”
把三类能力当作不同工具:
- REST/GraphQL 等一次性数据获取 → 用 Fetch 封装(现代、简洁、原生支持 AbortSignal、Stream)
- 需要兼容老浏览器或精细控制上传进度 → 单独保留 XHR 封装(例如上传大文件时监听 onprogress)
- 实时消息、状态同步、服务端推送 → WebSocket 单独建连接管理器,提供 subscribe / publish / reconnect 等语义方法
共用基础设施:拦截器、鉴权、错误标准化、Loading 状态
真正需要“统一”的,不是调用方式,而是横切关注点。例如:
- 所有请求自动注入
Authorizationheader(从 localStorage 或 auth store 读 token) - 401 响应统一触发登出流程;5xx 响应自动上报监控;网络异常统一降级提示
- 每个请求可声明是否开启全局 loading(
showLoading: true),由上层 UI 统一控制 spinner - WebSocket 断线重连时,自动恢复订阅主题(如
user:123),并补发离线期间的待发消息
定义清晰的数据契约与类型守卫
无论底层用什么发送,返回的数据结构应一致:
立即学习“Java免费学习笔记(深入)”;
- 统一响应格式:
{ code: number, data: any, message: string }(后端约定或前端适配) - 用 TypeScript interface 或 Zod schema 校验 response.data 结构,避免运行时取值报错
- WebSocket 收到的消息也走同一套解析管道(例如
onmessage中 parse 后 dispatch 到统一消息总线)
对外暴露语义化 API,隐藏实现细节
业务代码不关心“这次是 fetch 还是 xhr”,只关心“我要取用户资料”或“我要监听订单更新”:
-
api.user.getProfile()→ 内部用 fetch -
api.file.upload(file)→ 内部用 xhr(支持进度) -
realtime.order.subscribe(orderId, handler)→ 内部管理 ws 连接与 topic 订阅
这些 api 可以放在同一个 api 对象下,但内部各司其职,不共享执行逻辑。
不复杂但容易忽略:统一的价值不在“怎么发”,而在“怎么管”——管错误、管状态、管重试、管取消、管日志。把这层做扎实,比拼凑一个万能 request 函数有用得多。


















