Package Exports
- @xiaofeiwuuu/ws-client
Readme
@xiaofeiwuuu/ws-client
极简、零运行时依赖的浏览器 WebSocket 客户端 SDK。
- 🔌 开箱即用:内置一套官方协议,
createSocket({ url })即可,无需配置编解码。 - 🔁 自动重连:指数退避;被踢下线(close 4001)等自动不重连。
- 💓 心跳保活:ping/pong 探测"假死"连接,失活自动断开重连。
- 📨 请求-响应:
await socket.request(...),自带超时与业务错误。 - 🧩 类型推导:传入消息映射,
on/request的 payload 与返回值自动推导,拼错 type 直接编译报错。 - 🪶 可替换协议:需要 protobuf / msgpack 时覆盖
encode/decode即可。
通信协议契约见 PROTOCOL.md(语言无关,可直接交付后端)。
安装
pnpm add @xiaofeiwuuu/ws-client1. 快速上手
import { createSocket, BizError } from '@xiaofeiwuuu/ws-client'
const socket = createSocket({ url: 'wss://your-server/ws' })
// 订阅服务端推送,返回 off();不用记事件名配对,卸载时调一下即可
const off = socket.on('chat', (msg) => console.log('收到', msg))
// 只推,不等回包
socket.send('enterRoom', { roomId: 1 })
// 请求-响应:成功 / 超时 / 业务错误统一在一个 try/catch
try {
const list = await socket.request('getList', { page: 1 })
} catch (e) {
if (e instanceof BizError) console.log('业务失败', e.raw.code, e.raw.msg)
else console.log('超时或断线', (e as Error).message)
}
off() // 取消订阅不传 encode/decode 就用内置协议(见第 4 节),消息形如 { v:1, type, id?, payload? }。
2. 类型推导(强烈推荐)
给 createSocket 传两张映射表,on/request 全程类型安全:
// 服务端 → 你:推送消息(type -> payload)
interface ServerMessages {
chat: { from: string; text: string }
notify: { title: string; level: number }
}
// 你 → 服务端:请求-响应(type -> { req, res })
interface ApiMap {
getUser: { req: { id: number }; res: { id: number; name: string } }
login: { req: { token: string }; res: { uid: number } }
}
const socket = createSocket<ServerMessages, ApiMap>({ url: 'wss://...' })
socket.on('chat', (msg) => msg.text) // msg 自动是 { from, text }
socket.on('chatt', () => {}) // ❌ 编译报错:拼错 type
const u = await socket.request('getUser', { id: 1 }) // u 自动是 { id, name }不传泛型时一切照常宽松为 any,不影响使用。
3. 重连 / 订阅恢复 / 心跳
const socket = createSocket({
url: 'wss://...',
// 连接成功(每次都会调,isReconnect 区分首连/重连)
onOpen: (isReconnect) => { if (!isReconnect) subscribeTopics() },
// 断线重连成功后调用 —— 在这里重发服务端订阅(否则重连后收不到推送!)
onReconnect: () => subscribeTopics(),
onClose: (e) => console.log('closed', e.code),
onError: (err) => console.error(err.message),
reconnect: { baseDelay: 500, maxDelay: 10000, maxRetries: Infinity },
heartbeat: { interval: 15000, timeout: 5000 }, // 后端不支持 ping/pong 时设 { enabled: false }
// 自定义"哪些关闭码不重连"(默认 1000/4001/4002 不重连)
shouldReconnect: (e) => e.code !== 1000 && e.code !== 4001,
})
function subscribeTopics() {
socket.send('subscribe', { topic: 'chat' })
}订阅分两层:① 你
on()的回调存在连接之外,重连后自动还在;② 你告诉后端"我要某主题"的服务端订阅,后端断线即忘,必须在onReconnect里重发。⚠️ 心跳依赖后端实现 ping→pong。后端不回 pong 会被误判为假死并反复重连——这种情况请显式
heartbeat: { enabled: false }。
4. 内置协议与自定义
默认编解码(对齐 PROTOCOL.md v1):
// 发送:{ "v":1, "type":"getUser", "id":7, "payload":{...} }
// 响应:{ "v":1, "type":"getUser", "id":7, "code":0, "payload":{...} } // code!=0 → BizError
// 心跳:{ "v":1, "type":"ping" } / { "v":1, "type":"pong" }需要 protobuf / msgpack / 或对接已有协议时,覆盖这几个函数即可,其余逻辑不变:
createSocket({
url,
encode: (type, payload, id) => myEncode({ type, payload, id }), // 返回 string 或 ArrayBuffer
decode: (raw) => myDecode(raw), // 返回 { type, payload, id, code? }
isBizError: (msg) => msg.code !== 0,
})API 速查
| 方法 / 选项 | 说明 |
|---|---|
createSocket<S, A>(opts) |
创建连接,自动开始连接 + 心跳 |
socket.on(type, handler) |
订阅推送,返回 off() |
socket.send(type, payload?) |
只推,不等回包 |
socket.request(type, payload?, timeout=10000) |
请求-响应,返回 Promise |
socket.close() |
主动关闭,之后不再重连 |
opts.maxBuffer |
未连接时缓冲条数上限(默认 1000) |
opts.reconnect / opts.heartbeat |
重连 / 心跳配置 |
opts.onOpen/onClose/onError/onReconnect |
生命周期回调 |
环境
- 面向浏览器(使用全局
WebSocket)。 - 在 Node 中测试需自行 polyfill:
globalThis.WebSocket = require('ws')。
License
MIT