
基于 Hono 构建 WebSocket 回显服务并部署到 Vercelhono-websockets 示例全解析【免费下载链接】examplesEnjoy our curated collection of examples and solutions. Use these patterns to build your own robust and scalable applications.项目地址: https://gitcode.com/GitHub_Trending/examples1/examples本篇技术指南以仓库 websockets/hono 示例为蓝本讲解如何使用 Hono 框架在 Vercel 上搭建一个极简 WebSocket 服务一个 Hono 应用、一个/ws回显端点、一个可发送消息并渲染回显结果的 HTML 页面。读完本文你将掌握hono/node-server与ws的 WebSocket 集成方式、upgradeWebSocket的用法、NodeNext ESM 的工程配置以及从本地开发到一键部署的完整流程。示例概览最小化的 WebSocket 应用长什么样这个示例刻意保持极简全项目仅 3 个核心文件完整地呈现了服务端 客户端 工程配置的最小闭环服务端src/server.ts —— 一个 Hono 应用提供/页面路由、/health健康检查和/wsWebSocket 回显端点客户端public/index.html —— 一个零依赖的原生 WebSocket 客户端页面用户输入消息后发送服务端原样回显并渲染到页面工程配置package.json 与 tsconfig.json —— 定义依赖、脚本与 NodeNext ESM 编译规则。整个交互链路是浏览器通过WebSocket连接/ws→ 发送任意文本 → 服务端在onMessage中处理后通过ws.send()回显 → 页面监听message事件并把回显内容渲染为列表项。这个模式是理解 WebSocket 全双工通信的最佳入门样例也是后续扩展为聊天室、实时推送、协作编辑等应用的基础骨架。快速开始两种使用方式README 提供了两条使用路径你可以任选其一。方式一Vercel 一键部署README 中提供了 Vercel 的Deploy with Vercel一键部署按钮对应https://vercel.com/new/git/external的标准导入流程。点击后Vercel 会直接拉取该示例仓库、自动完成依赖安装与构建并为你分配一个可访问的线上地址部署完成后即可直接通过浏览器访问 WebSocket 页面。这种方式不需要本地安装任何 Node.js 工具链适合快速验证示例效果。方式二克隆到本地开发如果你希望本地运行和修改代码可以克隆本仓库后在websockets/hono目录下操作git clone https://gitcode.com/GitHub_Trending/examples1/examples cd examples/websockets/hono安装依赖项目使用 pnpm 作为包管理器pnpm-lock.yaml 已锁定依赖版本pnpm install启动本地开发服务器pnpm dev然后在浏览器打开 http://localhost:3000输入一条消息并发送服务端会把server: 你的消息原样回显到页面上。按CtrlC可停止服务。项目结构解析README 中给出了清晰的目录结构结合源码可展开如下websockets/hono/ ├── src/ │ └── server.ts # Hono 应用包含 / 页面路由与 /ws 回显端点 ├── public/ │ └── index.html # WebSocket 客户端 UI原生 JS零依赖 ├── tsconfig.json # NodeNext ESM 配置编译产物输出到 dist/ ├── package.json # 依赖、脚本与包元信息 └── pnpm-lock.yaml # 依赖版本锁定文件注意 tsconfig.json 中rootDir: src、outDir: dist的配置TypeScript 只编译src/目录下的源码public/下的静态 HTML 并不参与编译而是由服务端在运行时通过文件系统直接读取详见下文服务端解析因此部署时需要将public/一并带上。服务端源码深度解析src/server.ts 是整个示例的核心短短 40 余行代码串联起了静态页面服务、HTTP 健康检查与 WebSocket 升级三条链路。下面逐段拆解。导入与目录定位import { readFile } from node:fs/promises import { dirname, join } from node:path import { fileURLToPath, pathToFileURL } from node:url import { serve, upgradeWebSocket } from hono/node-server import { Hono } from hono import { WebSocketServer } from ws const __dirname dirname(fileURLToPath(import.meta.url))这里有一个值得注意的工程细节由于项目采用 ESMtype: module没有 CommonJS 的__dirname全局变量因此通过fileURLToPath(import.meta.url)手动计算当前模块目录。这一行代码是 NodeNext ESM 项目中的常见样板后续读取public/index.html时需要基于它拼接路径。静态页面与健康检查路由const app new Hono() app.get(/, async (c) { const html await readFile(join(__dirname, .., public, index.html), utf8) return c.html(html) }) app.get(/health, (c) c.json({ status: ok }))/路由在请求到达时用node:fs/promises的readFile异步读取public/index.html并通过c.html()以text/html响应返回。注意join(__dirname, .., public, index.html)的路径拼接当源码被编译到dist/后__dirname指向dist/..回退到项目根目录从而正确命中public/下的 HTML 文件/health返回{ status: ok }的 JSON可用于部署平台如 Vercel的健康检查或负载均衡的探活请求。WebSocket 端点upgradeWebSocket 的用法app.get( /ws, upgradeWebSocket(() ({ onMessage(event, ws) { const message String(event.data ?? ).trim() if (!message) return ws.send(server: ${message}) }, })), )这是整个示例最核心的代码。upgradeWebSocket是hono/node-server为 Hono 提供的 WebSocket 适配器它接收一个返回 WebSocket 事件处理器对象的工厂函数这里实现了onMessage回调onMessage中首先将event.data转为字符串并trim()去空白若消息为空则直接返回避免回显空串造成噪音非空消息则通过ws.send()发送回客户端前缀server:用于区分服务端回显与原始输入。从 Hono 的 API 设计看upgradeWebSocket工厂函数还可以返回onOpen、onClose、onError等生命周期回调本示例未使用它们分别对应连接建立、连接关闭和错误处理时机是扩展聊天室在线状态、心跳保活等能力的挂载点。底层 WebSocket 服务器集成const wss new WebSocketServer({ noServer: true }) const server serve({ fetch: app.fetch, port: Number(process.env.PORT || 3000), websocket: { server: wss }, }, (info) { if (import.meta.url pathToFileURL(process.argv[1]).href) { console.log(listening on http://localhost:${info.port}) } }) export default server这里揭示了hono/node-server与ws库的协作机制new WebSocketServer({ noServer: true })创建一个不自行监听端口的ws服务器实例。noServer: true意味着 WebSocketServer 只负责协议处理实际的 HTTP 服务器监听由serve()完成两者通过握手升级事件对接serve()是hono/node-server提供的 HTTP 服务器启动函数fetch: app.fetch把所有 HTTP 请求交给 Hono 应用处理port读取process.env.PORT环境变量Vercel 等平台会注入该变量未设置时默认回退到3000websocket: { server: wss }把前面创建的WebSocketServer挂载到 HTTP 服务器上使得/ws路由的升级请求能正确走 WebSocket 握手流程启动回调中通过import.meta.url pathToFileURL(process.argv[1]).href判断当前文件是否是被直接执行的入口只有直接运行src/server.tspnpm dev或dist/server.jspnpm start时才打印监听地址被作为模块导入时例如在测试中import server from ./server则不打印避免产生噪音日志export default server导出服务器实例方便其他模块引用或在测试中启停。回显服务的关键调用链小结一次完整的 WebSocket 回显请求调用链为浏览器发起ws://localhost:3000/ws握手 → HTTP 服务器将升级请求交给wssnoServer模式完成协议升级 → Hono 路由命中/ws的upgradeWebSocket处理器 → 之后每次客户端send()都会触发onMessage→ 服务端ws.send(server: message)回写 → 浏览器message事件渲染结果。客户端页面解析public/index.html 是一个零依赖的原生 HTML JavaScript 页面负责建立连接、发送消息与渲染回显逻辑清晰适合作为学习 WebSocket 客户端 API 的参考。自适应 WebSocket 地址const socket new WebSocket( ${location.protocol https: ? wss : ws}://${location.host}/ws )这一行代码体现了生产环境部署的重要细节根据当前页面协议自动选择wss加密或ws明文。本地开发走http://localhost:3000对应ws://部署到 Vercel 后页面为https://则自动切换为wss://从而避免明文 WebSocket 在 HTTPS 页面下被浏览器拦截Mixed Content 限制。连接状态与消息渲染socket.addEventListener(open, () { status.textContent Connected }) socket.addEventListener(close, () { status.textContent Disconnected }) socket.addEventListener(message, (event) { const item document.createElement(li) item.textContent event.data list.prepend(item) }) form.addEventListener(submit, (event) { event.preventDefault() socket.send(input.value) input.value })页面顶部#status段落展示连接状态初始为Connecting...连接建立后变为Connected关闭后变为Disconnectedmessage事件把event.data即服务端回显的server: xxx创建为li列表项并用list.prepend(item)插入到列表头部最新消息始终显示在最上方表单提交时preventDefault()阻止页面刷新然后socket.send()发送输入框内容并清空输入框。这段客户端代码完整对应了 README 描述的发送一条消息并渲染回显结果的行为且不依赖任何前端框架或构建工具可直接嵌入任意 HTML 页面。依赖与脚本说明package.json 中定义了完整的依赖与脚本类型包名版本作用dependencieshono4.12.26应用框架提供路由与中间件体系dependencieshono/node-server2.0.5Hono 的 Node.js 适配器提供serve与upgradeWebSocketdependenciesws8.21.0WebSocket 协议实现库负责底层握手与帧解析dependenciestypes/ws8.18.1ws的 TypeScript 类型声明devDependenciestypes/node24.13.2Node.js 运行时类型声明devDependenciestsx4.22.4直接运行 TypeScript 的开发工具devDependenciestypescript6.0.3TypeScript 编译器其中hono/node-server与ws是关键依赖组合serve()负责 HTTP 层upgradeWebSocket与WebSocketServer负责 WebSocket 层二者通过websocket: { server: wss }配置完成集成。三个 npm 脚本README 列出了三个脚本结合 package.json 中的定义可以明确其底层命令pnpm dev # 等价于 tsx watch src/server.ts以 watch 模式直接运行 TS 源码 pnpm build # 等价于 tsc按 tsconfig.json 将 src/ 编译为 dist/ pnpm start # 等价于 node dist/server.js运行编译后的产物dev使用tsx watch文件变更后自动重启且无需先编译TypeScript 源码直接被 tsx 转译执行适合开发迭代build调用tsc生成dist/目录产物为 ESM 格式的.js文件start运行编译产物适合生产环境。在 Vercel 上部署时平台会执行build并以start启动服务。工程配置NodeNext ESM 编译链路tsconfig.json 定义了 TypeScript 编译规则是理解源码如何变成可运行产物的关键{ compilerOptions: { target: ES2022, module: NodeNext, moduleResolution: NodeNext, lib: [ESNext, DOM], types: [node], rootDir: src, outDir: dist, strict: true, esModuleInterop: true, skipLibCheck: true, forceConsistentCasingInFileNames: true }, include: [src] }几个关键配置项的含义与影响module: NodeNext与moduleResolution: NodeNext遵循 Node.js 原生 ESM 的解析规则与package.json中的type: module配合产出和导入路径都使用 ESM 语义含.js扩展名。这正是源码中import.meta.url、fileURLToPath等 ESM 专有 API 可以正常工作的前提target: ES2022编译目标为 ES2022输出代码可直接运行在现代 Node.js 环境lib: [ESNext, DOM]同时包含 ESNext 与 DOM 类型库DOM 类型的存在是因为代码中使用了浏览器相关的全局类型如 HTML 页面的类型上下文types: [node]显式引入 Node.js 类型声明strict: true开启全部严格类型检查保证onMessage等回调中的类型安全rootDir: src/outDir: dist以src/为编译根目录、dist/为输出目录编译后dist/server.js即为 package.json 中main字段指向的入口文件。部署到 Vercel 的注意事项结合源码与部署流程有几点值得留意端口自动适配服务端通过process.env.PORT || 3000读取端口Vercel 会自动注入PORT环境变量本地默认3000与 README 中打开 http://localhost:3000的说明一致静态资源随包部署public/index.html不经过编译但/路由在运行时依赖它部署时需确保public/目录被包含在函数包中该示例项目结构简单不存在资源裁剪问题但若接入自定义构建流程需留意wss自动生效部署后页面为 HTTPS客户端代码会根据location.protocol自动使用wss://无需额外配置 TLS 证书健康检查/health端点返回 JSON可配合平台或外部监控进行探活。总结websockets/hono是一个麻雀虽小、五脏俱全的 WebSocket 示例它以最小代码量完整展示了 Hono hono/node-serverws的三层协作模型应用路由层、HTTP 适配层、WebSocket 协议层同时涵盖了 ESM 工程配置、静态页面服务、环境变量端口适配与一键部署等实战要素。将其作为起点你可以轻松扩展出消息广播遍历wss.clients、在线状态管理onOpen/onClose回调、房间隔离等更复杂的实时应用能力。【免费下载链接】examplesEnjoy our curated collection of examples and solutions. Use these patterns to build your own robust and scalable applications.项目地址: https://gitcode.com/GitHub_Trending/examples1/examples创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考