ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Wasp WebSocket 全栈集成实战指南:基于 Socket.IO 的实时通信与端到端类型安全

2026/9/14 8:45:24 拓冰建站 浏览量
Wasp WebSocket 全栈集成实战指南:基于 Socket.IO 的实时通信与端到端类型安全 Wasp WebSocket 全栈集成实战指南基于 Socket.IO 的实时通信与端到端类型安全【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/waspWasp 将 Socket.IO 深度集成进全栈框架开发者只需在.wasp配置中声明webSocket在服务端写一个事件处理函数客户端即可通过useSocket/useSocketListener两个 React Hook 直接收发事件URL 组装、CORS、连接生命周期与身份注入全部由框架接管。读完本文你将掌握在 Wasp 0.12 中从零搭建实时聊天/投票应用、为 WebSocket 事件声明全栈类型安全以及对照源码理解其底层接线原理的完整能力。一、Wasp 的 WebSocket 集成方式概述Wasp 提供“开箱即用”batteries-included的 WebSocket 体验底层协议引擎是 Socket.IO。框架在服务端与客户端两侧都内置了 Socket.IO 支持并替你处理了三件最容易出错的事URL 自动配置客户端无需手写服务端地址WebSocketProvider直接复用 Wasp 生成的config.apiUrl建立连接CORS 自动开启服务端new Server(...)时自动将config.frontendUrl设为cors.originReact 抽象提供useSocket与useSocketListener两个 Hook封装连接的建立、状态上报与事件监听/卸载。要启用 WebSocket只需四步在服务端定义 WebSocket 事件处理逻辑在 Wasp 配置文件的app中声明webSocket并绑定服务端函数在客户端 React 组件中通过useSocket、useSocketListener使用可选为事件与载荷声明 TypeScript 类型获得全栈类型安全。下文从第 2 步配置文件开始逐步展开。二、在 Wasp 配置文件中开启 WebSocket在app声明中新增webSocket字典并为其提供必需的fn服务端函数引用。autoConnect为可选项控制客户端是否自动建立连接默认值为true。app todoApp { // ... webSocket: { fn: import { webSocketFn } from src/webSocket, autoConnect: true, // optional, default: true }, }这里的语法是经典的.waspDSL 形式import { webSocketFn } from src/webSocket指向src/webSocket.js或.ts中导出的函数。需要注意的是当前仓库的示例项目如 examples/websockets-realtime-voting/main.wasp.ts已采用新一代 TypeScript 规范TS Spec写法声明等价export default app({ // ... webSocket: { fn: votingWebSocket, }, // ... });从 AppSpec 的解析模型可以看出webSocket字典只有两个字段fnExtImport服务端函数的外部导入引用与可选的autoConnectMaybe Bool见 waspc/src/Wasp/AppSpec/App/WebSocket.hs。生成器据此判断“是否启用了 WebSocket”只要app.webSocket存在即视为启用见 waspc/src/Wasp/Generator/WebSocket.hs。webSocket配置字段速查字段类型必填说明fnWebSocketFn是定义 WebSocket 事件与处理函数的服务端函数autoConnectbool否客户端是否自动连接 WebSocket 服务端默认true依赖自动注入启用 WebSocket 后生成器会为服务端与 SDK 自动声明所需的 npm 依赖版本范围为^4.6.1无需手动安装服务端socket.io、socket.io/component-emitterSDK 侧额外增加socket.io-client。这一逻辑定义在 waspc/src/Wasp/Generator/WebSocket.hs并由 waspc/src/Wasp/Generator/ServerGenerator/WebSocketG.hs 与 waspc/src/Wasp/Generator/SdkGenerator/WebSocketGenerator.hs 分别装配进服务端与 SDK 的 package.json。三、定义服务端事件处理函数webSocketFn在src/webSocket.js或.ts中定义服务端 WebSocket 逻辑。函数签名为(io, context) voidioSocket.IO 的Server实例可用它注册connection回调及所有标准 Socket.IO 事件context提供 Wasp 应用的全部实体entities可直接在事件回调中使用例如context.entities.SomeEntity.create(...)。若用户已登录服务端 socket 上还会附带socket.data.user。JavaScript 版本import { v4 as uuidv4 } from uuid import { getFirstProviderUserId } from wasp/auth export const webSocketFn (io, context) { io.on(connection, (socket) { const username getFirstProviderUserId(socket.data.user) ?? Unknown console.log(a user connected: , username) socket.on(chatMessage, async (msg) { console.log(message: , msg) io.emit(chatMessage, { id: uuidv4(), username, text: msg }) // You can also use your entities here: // await context.entities.SomeEntity.create({ someField: msg }) }) }) }要点解析getFirstProviderUserId(socket.data.user)从当前登录用户的 Auth 实体中取出用户名?? Unknown处理匿名兜底socket.on(chatMessage, ...)监听单个客户端事件io.emit(chatMessage, ...)广播给所有连接事件回调是async的因此在其中可以安全地await context.entities.*做数据库操作。TypeScript 版本与全栈类型安全TS 版本的关键在于用WebSocketDefinition泛型显式声明四组类型参数从而在服务端定义事件契约客户端自动继承import { v4 as uuidv4 } from uuid import { getFirstProviderUserId } from wasp/auth import { type WebSocketDefinition, type WaspSocketData } from wasp/server/webSocket export const webSocketFn: WebSocketFn (io, context) { io.on(connection, (socket) { const username getFirstProviderUserId(socket.data.user) ?? Unknown console.log(a user connected: , username) socket.on(chatMessage, async (msg) { console.log(message: , msg) io.emit(chatMessage, { id: uuidv4(), username, text: msg }) // You can also use your entities here: // await context.entities.SomeEntity.create({ someField: msg }) }) }) } // Typing our WebSocket function with the events and payloads // allows us to get type safety on the client as well type WebSocketFn WebSocketDefinition ClientToServerEvents, ServerToClientEvents, InterServerEvents, SocketData interface ServerToClientEvents { chatMessage: (msg: { id: string, username: string, text: string }) void; } interface ClientToServerEvents { chatMessage: (msg: string) void; } interface InterServerEvents {} // Data that is attached to the socket. // NOTE: Wasp automatically injects the JWT into the connection, // and if present/valid, the server adds a user to the socket. interface SocketData extends WaspSocketData {}WebSocketDefinition的类型定义可在 SDK 模板中看到它约束了io的四个泛型参数并声明context.entities由 Wasp 生成的全部实体组成export type WebSocketDefinition ClientToServerEvents extends EventsMap DefaultEventsMap, ServerToClientEvents extends EventsMap DefaultEventsMap, InterServerEvents extends EventsMap DefaultEventsMap, SocketData extends WaspSocketData WaspSocketData ( io: ServerClientToServerEvents, ServerToClientEvents, InterServerEvents, SocketData, context: { entities: { /* 所有实体 */ } } ) Promisevoid | void见 waspc/data/Generator/templates/sdk/wasp/server/webSocket/index.ts。关于SocketData还有一处值得注意的框架行为Wasp 会自动把当前会话的 sessionId 注入握手认证socket.auth服务端若校验通过会把用户对象附加到socket.data.user上。WaspSocketData接口正是为此预留的结构启用 auth 时包含可选字段user?: AuthUser见 waspc/data/Generator/templates/sdk/wasp/server/webSocket/index.ts。其底层实现是服务端初始化模板中的中间件addUserToSocketDataIfAuthenticated读取socket.handshake.auth.sessionId经会话查询得到用户后再写入socket.data见 waspc/data/Generator/templates/server/src/webSocket/initialization.ts。四、客户端使用useSocket与useSocketListener客户端 WebSocket 能力从wasp/client/webSocket导入核心是WebSocketProvider 两个 HookSDK 源码见 waspc/data/Generator/templates/sdk/wasp/client/webSocket/index.ts 与 WebSocketProvider.tsx。useSocketHookuseSocket()返回一个对象socket: Socket用于发送与接收事件的 Socket.IO 客户端实例isConnected: booleanSocket.IO 连接状态适合在界面上展示连接指示灯。两点重要行为默认自动连接Wasp 默认会自动建立客户端到服务端的 WebSocket 连接无需手动调用socket.connect()/socket.disconnect()autoConnect: false时若你在 Wasp 文件中关闭了自动连接则需按需自行调用这两个方法。此外所有使用useSocket的组件共享同一个底层socket单例。从模板源码可以看到socket是在模块顶层通过io(config.apiUrl, { transports: [websocket], autoConnect: ... })创建的export const socket: SocketServerToClientEvents, ClientToServerEvents io( config.apiUrl, { transports: [websocket], autoConnect: { autoConnect } !import.meta.env.SSR, } )见 WebSocketProvider.tsx。其中{ autoConnect }是模板占位符由 SDK 生成器根据webSocket.autoConnect配置填充——只有未显式设置为false时才为true见 waspc/src/Wasp/Generator/SdkGenerator/WebSocketGenerator.hs。同时!import.meta.env.SSR保证了 SSR 场景下不会在服务端创建 WebSocket 连接。模板还在模块加载时调用refreshAuthToken()把当前sessionId写入socket.auth并监听sessionId.set/sessionId.clear事件在登录/登出后自动重连以刷新认证信息见 WebSocketProvider.tsx。useSocketListenerHookuseSocketListener: (event, callback) void用于注册事件处理器并在组件卸载时自动注销监听无需手动清理。useSocketListener(chatMessage, logMessage)其实现基于useEffect在 effect 中执行socket.on(event, handler)并返回socket.off(event, handler)作为清理函数依赖为[event, handler]见 index.ts。完整聊天页面示例JavaScript 版本import React, { useState } from react import { useSocket, useSocketListener, } from wasp/client/webSocket export const ChatPage () { const [messageText, setMessageText] useState() const [messages, setMessages] useState([]) const { socket, isConnected } useSocket() useSocketListener(chatMessage, logMessage) function logMessage(msg) { setMessages((priorMessages) [msg, ...priorMessages]) } function handleSubmit(e) { e.preventDefault() socket.emit(chatMessage, messageText) setMessageText() } const messageList messages.map((msg) ( li key{msg.id} em{msg.username}/em: {msg.text} /li )) const connectionIcon isConnected ? : return ( h2Chat {connectionIcon}/h2 div form onSubmit{handleSubmit} div div input typetext value{messageText} onChange{(e) setMessageText(e.target.value)} / /div div button typesubmitSubmit/button /div /div /form ul{messageList}/ul /div / ) }TypeScript 版本全栈类型安全生效TS 场景下所有事件与载荷类型会自动从服务端推断并下发到客户端。在 VS Code 中事件名与载荷都会有自动补全写错事件名或载荷类型会直接得到类型错误。你还可以使用两个辅助类型来获取指定事件的载荷类型ClientToServerPayloadeventName客户端 → 服务端某事件的载荷类型ServerToClientPayloadeventName服务端 → 客户端某事件的载荷类型。这两个辅助类型的定义为ParametersEvents[Event][0]见 index.ts。import React, { useState } from react import { useSocket, useSocketListener, ServerToClientPayload, } from wasp/client/webSocket export const ChatPage () { const [messageText, setMessageText] useState // We are using a helper type to get the payload type for the chatMessage event. ClientToServerPayloadchatMessage () const [messages, setMessages] useState ServerToClientPayloadchatMessage[] ([]) // The socket instance is typed with the types you defined on the server. const { socket, isConnected } useSocket() // This is a type-safe event handler: chatMessage event and its payload type // are defined on the server. useSocketListener(chatMessage, logMessage) function logMessage(msg: ServerToClientPayloadchatMessage) { setMessages((priorMessages) [msg, ...priorMessages]) } function handleSubmit(e: React.FormEventHTMLFormElement) { e.preventDefault() // This is a type-safe event emitter: chatMessage event and its payload type // are defined on the server. socket.emit(chatMessage, messageText) setMessageText() } const messageList messages.map((msg) ( li key{msg.id} em{msg.username}/em: {msg.text} /li )) const connectionIcon isConnected ? : return ( h2Chat {connectionIcon}/h2 div form onSubmit{handleSubmit} div div input typetext value{messageText} onChange{(e) setMessageText(e.target.value)} / /div div button typesubmitSubmit/button /div /div /form ul{messageList}/ul /div / ) }注意ClientToServerPayload在示例代码中已使用但未在import中显式列出实际使用时请一并从wasp/client/webSocket导入。五、源码视角WebSocket 是如何被接线的理解生成器如何把配置“翻译”成运行时代码有助于排查问题与扩展高级用法。服务端接线生成器在检测到app.webSocket后基于模板 server/src/webSocket/initialization.ts 生成服务端初始化代码见 waspc/src/Wasp/Generator/ServerGenerator/WebSocketG.hs初始化逻辑new Server(server, { cors: { origin: config.frontendUrl } })绑定到 HTTP server自动开启对前端地址的 CORS无需手写跨域配置随后启用 auth 时注册鉴权中间件把用户注入socket.data再构造context { entities: { ...所有实体 } }最后调用你的webSocketFn(io, context)见 initialization.tsgenWebSockets仅在启用了 WebSocket 时才生成上述文件否则为空列表并在ServerGenerator的生成流程中被调用见 waspc/src/Wasp/Generator/ServerGenerator.hs。SDK 侧接线SDK 生成器同样按需生成三类文件服务端类型索引server/webSocket/index.ts、客户端 Hookclient/webSocket/index.ts与 Providerclient/webSocket/WebSocketProvider.tsx见 waspc/src/Wasp/Generator/SdkGenerator/WebSocketGenerator.hs。这些文件会在SdkGenerator主流程中与其余 SDK 文件一并产出见 waspc/src/Wasp/Generator/SdkGenerator.hs。依赖版本框架固定 Socket.IO 相关依赖的版本范围为socket.io ^4.6.1、socket.io/component-emitter ^4.0.0统一在 waspc/src/Wasp/Generator/WebSocket.hs 中声明保证服务端与客户端协议版本一致。六、实战参考仓库内的实时投票示例当前仓库自带一个完整的 WebSocket 实时应用示例 examples/websockets-realtime-voting可作为对照实现配置声明app中声明webSocket: { fn: votingWebSocket }并启用了usernameAndPassword认证与authRequired页面见 examples/websockets-realtime-voting/main.wasp.ts服务端votingWebSocket用WebSocketDefinitionClientToServerEvents, ServerToClientEvents, InterServerEvents声明vote、askForStateUpdate两个客户端事件与updateState服务端事件在内存中维护轮询状态支持“投票/改票”见 examples/websockets-realtime-voting/src/ws-server.ts客户端MainPage.tsx通过useSocketListener(updateState, ...)订阅状态、socket.emit(vote, optionId)投票并利用ServerToClientPayloadupdateState获得状态载荷的完整类型见 examples/websockets-realtime-voting/src/pages/MainPage.tsx端到端验证e2e-tests/tests/simple.spec.ts用 Playwright 验证了“注册 → 登录 → 投票 → 界面出现用户名、按钮变为 Voted 且禁用、票数更新”的完整链路见 examples/websockets-realtime-voting/e2e-tests/tests/simple.spec.ts可作为自己应用集成测试的模板。值得注意示例服务端在connection回调中先检查socket.data.user未登录连接直接返回——这是匿名连接控制的一种实用做法服务端以io.emit广播updateState保证所有客户端实时看到投票结果。七、常见问题与最佳实践不需要手动管理连接默认autoConnect: true登录/登出后框架会依据sessionId事件自动刷新认证并重连只有显式设置autoConnect: false时才需要手动socket.connect()/socket.disconnect()。连接状态展示用isConnected驱动 UI 指示灯如聊天页的 /其状态由 Provider 在connect/disconnect事件中维护初始为false保证 SSR 安全见 WebSocketProvider.tsx。类型安全优先用 TS把事件契约定义在服务端WebSocketDefinition中客户端事件名与载荷即可获得自动补全与编译期校验避免“拼错事件名”这类运行时低级错误。在事件回调中使用数据库context.entities可用且回调为async可直接持久化业务数据不必再额外发起 RPC。事件监听清理优先使用useSocketListener它已封装好卸载时注销若在组件中直接socket.on请自行在useEffect清理避免内存泄漏与重复回调。至此从配置声明、服务端事件定义、客户端 Hook 使用到源码级接线原理你已具备在 Wasp 0.12 中构建实时功能聊天、投票、通知、协作编辑等的完整知识。更详细的 API 字段与版本差异可继续查阅本文档原文 web/versioned_docs/version-0.12/advanced/web-sockets.md以及仓库内更新的版本化文档 web/docs/advanced/web-sockets.md。【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/wasp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考