完全指南:端到端类型安全的订阅连接与参数配置)
tRPC WebSocket LinkwsLink完全指南端到端类型安全的订阅连接与参数配置【免费下载链接】trpc♀️ Move Fast and Break Nothing. End-to-end typesafe APIs made easy.项目地址: https://gitcode.com/GitHub_Trending/tr/trpc导读wsLink是 tRPC 客户端链接体系中的终止型链接terminating link专用于走 WebSocket 协议承载的**实时订阅subscriptions**与双向通信场景配合trpc/server的 WebSocket 适配器即可在客户端获得与 HTTP 查询一致的全链路类型安全体验。读完本文你将掌握如何用createWSClient构造 WebSocket 客户端并接入createTRPCClient、如何配置认证连接参数connection params、重连退避、懒连接lazy与心跳保活keepAlive等全部选项并能结合 tRPC v11 仓库源码理解其底层连接管理与请求批处理机制。一、wsLink 在 tRPC 链接模型中的定位在 tRPC 客户端中links是一个类似中间件管道的数组其中最后一个链接必须是终止型链接terminating link负责真正把操作operation发往服务端。关于终止链接的完整定义可参见 overview.md。wsLink正是这样一种终止型链接它在两种场景下被使用WebSockets Client基于trpc/server/adapters/ws适配器、以 WebSocket 为传输层建立连接的客户端Subscriptions订阅客户端发起subscription类型操作并持续接收服务端推送这是 HTTP 无法天然支撑、必须依赖长连接的能力。关于订阅的服务器端模型订阅如何定义、如何发布事件请参考 subscriptions.md。关于服务端 WebSocket 适配器wsServer、applyWSSHandler、createContext中的info以及连接参数的完整契约请参考 websockets.md。从 v11 的源码看wsLink位于 packages/client/src/links/wsLink/wsLink.ts内部通过observable包装操作并将connectionState连接状态以推送流的形式暴露给订阅型操作让客户端在断线重连时也能持续收到状态事件。二、快速上手最小可用配置要使用wsLink你首先需要通过createWSClient创建一个TRPCWebSocketClient再把它传给wsLinkimport { createTRPCClient, createWSClient, wsLink } from trpc/client; import type { AppRouter } from ./server; // 1. 创建 WebSocket 客户端连接管理、自动重连、心跳都由它负责 const wsClient createWSClient({ url: ws://localhost:3000, }); // 2. 将 wsLink 作为唯一或链尾的终止链接注入客户端 const trpcClient createTRPCClientAppRouter({ links: [wsLinkAppRouter({ client: wsClient })], }); // 之后即可对订阅调用发起类型安全的调用 // const sub trpcClient.posts.onAdd.subscribe(undefined, { // onData: (post) console.log(new post:, post), // });其中wsLink、createWSClient均由trpc/client统一导出见 packages/client/src/links/wsLink/wsLink.ts 中的重新导出。wsLink的泛型参数会依据你的AppRouter推导保证订阅路径、输入与输出类型全部受检。在实际的仓库示例中可参考 examples/standalone-server/src/client.ts、examples/next-sse-chat 等目录它们演示了客户端如何与服务端适配器搭配完成订阅闭环。三、WebSocketLinkOptions 与 WebSocketClientOptions 全参数详解wsLink需要一个TRPCWebSocketClient该客户端的所有行为由WebSocketClientOptions定义。下面给出完整类型定义与 packages/client/src/links/wsLink/wsClient/options.ts 保持一致的 v11 接口并逐一说明语义export interface WebSocketLinkOptions { client: TRPCWebSocketClient; // 由 createWSClient() 创建的客户端 /** * 数据转换器transformer * 例如 superjson / devalue需与服务端保持一致 */ transformer?: DataTransformerOptions; } export interface WebSocketClientOptions { /** * 要连接的地址也可以是返回 URL 的函数支持异步 */ url: string | (() MaybePromisestring); /** * 连接参数。会作为「第一条消息」发送给服务端 * 服务端可在 createContext() 的 opts.info.connectionParams 中读取。 * 支持静态对象、函数或异步函数。 */ connectionParams?: | Recordstring, string | null | (() MaybePromiseRecordstring, string | null); /** * WebSocket 的 ponyfill 实现。 * 在 Node.js 等无原生 WebSocket 的环境中必须传入例如 ws 包。 */ WebSocket?: typeof WebSocket; /** * 断线后重连前的等待毫秒数按尝试次数计算。 * 默认使用 exponentialBackoff指数退避。 */ retryDelayMs?: typeof exponentialBackoff; /** WebSocket 连接建立成功时触发 */ onOpen?: () void; /** WebSocket 连接出错时触发 */ onError?: (evt?: Event) void; /** WebSocket 连接关闭时触发 */ onClose?: (cause?: { code?: number }) void; /** * 懒连接模式在一段时间内没有消息收发且无挂起请求时 * 自动关闭 WebSocket下次请求时再按需打开。 */ lazy?: { /** 是否启用懒连接默认 false */ enabled: boolean; /** 空闲多久毫秒后关闭连接默认 0 */ closeMs: number; }; /** * 心跳保活周期性发送 ping若在超时时间内未收到 pong 则断开连接。 */ keepAlive?: { /** 是否启用默认 false */ enabled: boolean; /** 每隔多少毫秒发送一次 ping默认 5_000 */ intervalMs?: number; /** 若超过多少毫秒未收到服务端响应则关闭连接默认 1_000 */ pongTimeoutMs?: number; }; /** * 自定义线上传输编解码器例如二进制格式。 * 默认 jsonEncoder。 */ experimental_encoder?: Encoder; }WebSocketLinkOptions相比旧版增加了transformer字段而client必须为TRPCWebSocketClient类型——两者的类型约束分别定义于 wsLink.ts 与 options.ts。3.1 url静态地址或动态解析url既可以写死为字符串也可以传一个返回地址的函数。函数形式常用于先拿 token 再拼 URL或按环境切换网关地址的场景。该类型定义位于 urlWithConnectionParams.ts。3.2 指数退避与 retryDelayMs默认重连策略为exponentialBackoff其实现位于 options.tsexport const exponentialBackoff (attemptIndex: number) { return attemptIndex 0 ? 0 : Math.min(1000 * 2 ** attemptIndex, 30000); };即第一次重连立即执行delay 为 0此后每次尝试的延迟按1000 × 2^attemptIndex毫秒指数增长并封顶在 30 秒。需要自定义策略时如固定 1 秒重连一次传入形如(attemptIndex) number的函数即可。3.3 keepAlive 心跳保活原理keepAlive.enabled开启后客户端会周期性发送PING文本帧服务端回PONG若在pongTimeoutMs内没等到响应则主动关闭连接以触发重连。其默认值常量在源码中为export const keepAliveDefaults { enabled: false, pongTimeoutMs: 1_000, intervalMs: 5_000, };对应到 options.ts。底层定时器与PING/PONG收发逻辑位于 wsConnection.ts 的setupPingInterval函数每次收到任意消息都会重置定时器收到PONG会清除超时标记并重新计时。服务端适配器packages/server/src/adapters/ws/同样内置对PING的PONG回包处理。3.4 lazy 懒连接模式开启lazy后客户端构造时不会立即建立连接connectionState初始为idle当有请求到来时才调用open()建立连接。空闲超过closeMs且没有出站/挂起请求后自动close()以节省资源。export const lazyDefaults { enabled: false, closeMs: 0 };其默认常量与关闭条件判断实现可分别见 options.ts 与 wsClient.ts。注意若开启了懒连接且正处于空闲关闭状态connectionState.state会回到idle当存在活动订阅时即使空闲客户端也不会把连接关闭见 wsClient.ts 的hasPendingSubscriptions判断。3.5 experimental_encoder自定义线上格式默认jsonEncoder使用 JSON 文本帧其decode在收到二进制数据时会直接抛错见 encoder.ts。如果需要自定义二进制协议可传入实现{ encode(data): TInput, decode(message): TOutput }的编码器——服务端需配套使用相同的 encodertrpc/server/adapters/ws的Encoder类型定义相关双向验证可参见 websockets.encoder.test.ts。仓库同时也导出jsonEncoder便于服务端引用。3.6 transformer数据转换器WebSocketLinkOptions.transformer用于配置与服务端一致的数据转换器如 superjson、devalue保证序列化 Date、Map 等复杂类型。它由getTransformer(opts.transformer)归一化为客户端内部统一的CombinedDataTransformer见 wsLink.ts并在每次请求时用transformer.input.serialize(input)序列化入参、以transformResult(event, transformer.output)反序列化出参见 wsClient.ts。若服务端启用了 transformer客户端未配置将导致解析失败。四、Authentication / Connection Params连接参数认证4.1 客户端如何发送在createWSClient中提供connectionParams它支持静态对象、返回对象的函数乃至异步函数const wsClient createWSClient({ url: ws://localhost:3000, connectionParams: async () { return { token: supersecret, }; }, });关键实现细节connection params不是随 HTTP 头传递的而是在 WebSocket 握手成功后的第一条消息中发送。底层由 wsConnection.ts 的open()流程处理——连接就绪后调用buildConnectionMessage(urlOptions.connectionParams, encoder)并ws.send(...)。其消息结构形如interface ConnectionParamsMessage { data: Recordstring, string | null; method: connectionParams; }4.2 服务端如何读取与鉴权服务端在 WebSocket 适配器的createContext中通过opts.info.connectionParams读取示例详见 websockets.md 的 Authentication/connection params 一节import type { CreateWSSContextFnOptions } from trpc/server/adapters/ws; export const createContext async (opts: CreateWSSContextFnOptions) { const token opts.info.connectionParams?.token; // [... 校验 token把用户信息挂到 ctx 上] return { userId: ... }; };4.3 两条重要提示浏览器 Web 应用通常无需此机制Cookie 会随 WebSocket 握手的 HTTP Upgrade 请求自动携带服务端可直接通过会话 Cookie 鉴权HTTP 类订阅链接的差异httpSubscriptionLink也支持connectionParams但它是被序列化进 URL 的connectionParamsquery 参数传递的见 urlWithConnectionParams.ts 中的说明而非首条消息。五、深入源码wsLink 的底层工作原理5.1 wsLink订阅连接状态 转发请求wsLink本身是一个很薄的封装wsLink.ts。对于订阅型操作它会额外订阅client.connectionState把connecting/pending等连接状态事件作为结果推送对于查询与变更则直接调用client.request({ op, transformer })。当 observer 被取消时会同时退订连接状态与请求订阅保证不泄漏。5.2 WsClient请求批处理、重连与生命周期createWSClient返回的是WsClient实例createWsClient.ts 与 wsClient.ts。它的职责包括连接状态机通过behaviorSubject对外暴露TRPCConnectionState其状态为idle | connecting | pending三种见 subscriptions.ts。非懒模式下构造后立即置为connecting并尝试open()请求批处理batchSend同一宏任务内到达的多个操作会被暂存在RequestManager中随后在send阶段合并为一次发送对入参做sleep(0)等待以完成同批次收集见 wsClient.ts自动重连连接被关闭或出错时调用reconnect()依据retryDelayMs默认指数退避休眠后重建连接并把连接期间积压的挂起请求补发出去单个重连循环通过reconnectingpromise 保证不会并发执行wsClient.ts服务端主动重连收到{ method: reconnect }类型的入站消息时客户端也会主动触发重连handleIncomingRequest订阅停止当订阅 observable 被取消且连接仍开启时客户端会发送{ id, method: subscription.stop }通知服务端释放资源wsClient.ts。5.3 WsConnection单连接管理与心跳每个WsClient内部持有一个WsConnectionwsConnection.ts负责Ponyfill 注入若未传入WebSocket且运行环境没有原生实现如 Node.js构造时会抛出明确的错误提示指引你传入ws等 ponyfillwsConnection.ts连接去重openPromise保证同一时间只进行一次连接握手避免并发open()二进制帧支持将binaryType设为arraybuffer使二进制 encoder 的帧能正确到达 decode 层服务端 ping 应答收到PING文本帧时立即回发PONG。六、测试与验证资源仓库为 WebSocket 客户端行为提供了完整的测试覆盖是理解边缘行为的绝佳参考websockets.test.ts覆盖连接、订阅推送、断线重连、subscription.stop等核心场景websockets.encoder.test.ts验证自定义 encoder 的二进制编解码是否在客户端与服务端之间正确往返websockets.memory.test.ts通过内存传输模拟连接检验懒连接与状态机的内存安全transformer.test.ts验证 transformer 在 WebSocket 链路上的序列化/反序列化。若需在 Node.js 环境直接体验客户端可对照 standalone-server 与 lambda-api-gateway-streaming 等示例工程查看完整的客户端/服务端配对写法。七、小结wsLink是订阅场景的终止链接必须与createWSClient返回的TRPCWebSocketClient搭配使用并作为createTRPCClient的links数组终止项。客户端能力全部由WebSocketClientOptions驱动动态url、首条消息发送的connectionParams认证、指数退避retryDelayMs、生命周期回调、lazy懒连接与keepAlive心跳、可插拔的experimental_encoder。想深入了解服务端适配、连接参数契约以及如何发起订阅请继续阅读 websockets.md 与 subscriptions.md。【免费下载链接】trpc♀️ Move Fast and Break Nothing. End-to-end typesafe APIs made easy.项目地址: https://gitcode.com/GitHub_Trending/tr/trpc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考