JSPM

@xiaofeiwuuu/ws-client

0.1.0
    • ESM via JSPM
    • ES Module Entrypoint
    • Export Map
    • Keywords
    • License
    • Repository URL
    • TypeScript Types
    • README
    • Created
    • Published
    • Downloads 150
    • Score
      100M100P100Q63864F
    • License MIT

    极简、零依赖的浏览器 WebSocket 客户端 SDK:内置协议、自动重连、心跳、请求-响应、类型推导。

    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-client

    1. 快速上手

    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