Nuxt 中集成 Socket.IO 全解:socket.io 官方示例的开发、构建与生产预览实战
本篇基于 socket.io 仓库官方的 Nuxt 示例,完整讲解如何将 Socket.IO 的 WebSocket 双向通信能力嵌入 Nuxt 4 服务端(Nitro 引擎),并覆盖从依赖安装、开发服务器启动,到生产构建与本地预览的完整流程。读完后你将掌握:如何在 Nuxt 中通过 Nitro 插件挂载 socket.io 服务端、如何让 HTTP 轮询与 WebSocket 升级请求正确路由到 engine.io,以及客户端如何创建全局 socket 并展示连接状态。
示例定位与文件结构
该示例是 Nuxt Minimal Starter 的 socket.io 集成版本,用于演示在 Nuxt 全栈框架中运行 Socket.IO 服务端与客户端的最小可行方案。核心文件职责如下:
| 文件 | 职责 |
|---|---|
| nuxt.config.ts | Nuxt 配置,开启 Nitro 的 WebSocket 实验特性 |
| server/plugins/socket.io.ts | Nitro 插件:创建并挂载 socket.io 服务端 |
| app/components/socket.ts | 客户端:创建全局 socket 单例 |
| app/components/Connection.client.vue | 客户端组件:展示连接状态与当前传输层 |
| app/app.vue | 应用入口,渲染 <Connection /> 组件 |
| package.json | 依赖与 npm scripts 定义 |
一、环境准备:安装依赖
按照 README 的 Setup 章节,先安装依赖。官方支持 npm、pnpm、yarn、bun 四种包管理器,任选其一:
# npm
npm install
# pnpm
pnpm install
# yarn
yynarn install
# bun
bun install
注意:
bun与yarn分支下应分别执行bun install与yarn install。
从 package.json 可以看到示例锁定(范围声明)的核心依赖版本,可作为集成时选型参考:
| 依赖 | 版本范围 | 说明 |
|---|---|---|
nuxt |
^4.4.6 |
Nuxt 4,内置 Nitro 服务端引擎 |
socket.io |
^4.8.3 |
服务端库 |
socket.io-client |
^4.8.3 |
客户端库(与同版本服务端配套) |
vue |
^3.5.34 |
Vue 3 |
vue-router |
^5.0.7 |
路由 |
另外 "postinstall": "nuxt prepare" 会在每次安装后自动执行 nuxt prepare,生成 .nuxt 目录下的类型声明,使 server/tsconfig.json 中 extends "../.nuxt/tsconfig.server.json" 能够正常解析,这是 Nitro 服务端代码获得类型提示的前提。
二、启动开发服务器
安装完成后启动开发服务器,默认监听 http://localhost:3000:
# npm
npm run dev
# pnpm
pnpm dev
# yarn
yarn dev
# bun
bun run dev
这些脚本对应 package.json 中的 "dev": "nuxt dev"。启动后,Nuxt 会扫描 server/plugins/ 目录并执行其中的 Nitro 插件(即下一节详述的 socket.io 挂载逻辑),随后客户端即可通过浏览器访问首页看到连接状态面板。
三、关键配置:开启 Nitro 的 WebSocket 实验特性
整个示例能跑通 WebSocket 升级(从 polling 升级为 websocket 传输层)的关键,在 nuxt.config.ts 中:
export default defineNuxtConfig({
compatibilityDate: '2025-07-15',
devtools: {
enabled: true
},
nitro: {
experimental: {
websocket: true
},
}
})
逐段说明:
compatibilityDate: '2025-07-15':Nuxt 4 的兼容性基线日期,用于锁定 Nitro/Vite 的行为版本;devtools.enabled: true:开启 Nuxt DevTools 开发工具;nitro.experimental.websocket: true:这是示例的核心开关。Nitro 默认不处理 WebSocket 升级请求,开启该实验特性后,Nitro 内置的 HTTP 服务器才会支持将Upgrade: websocket请求交给插件声明的websocket.open回调处理——没有这一行,Socket.IO 只能停留在 HTTP 轮询,无法完成传输层升级。
四、服务端核心:用 Nitro 插件挂载 Socket.IO
server/plugins/socket.io.ts 是服务端全部逻辑所在,完整代码如下:
import type { NitroApp } from "nitropack";
import { Server as Engine } from "engine.io";
import { Server } from "socket.io";
import { defineEventHandler } from "h3";
export default defineNitroPlugin((nitroApp: NitroApp) => {
const engine = new Engine();
const io = new Server();
io.bind(engine);
io.on("connection", (socket) => {
// ...
});
nitroApp.router.use("/socket.io/", defineEventHandler({
handler(event) {
engine.handleRequest(event.node.req, event.node.res);
event._handled = true;
},
websocket: {
open(peer) {
// crossws >= 0.3.0
// @ts-expect-error private method and property
engine.prepare(peer._internal.nodeReq);
// @ts-expect-error private method and property
engine.onWebSocket(peer._internal.nodeReq, peer._internal.nodeReq.socket, peer.websocket);
// crossws < 0.3.0
// const context = peer.ctx.node;
// // @ts-expect-error private method
// engine.prepare(context.req);
// // @ts-expect-error private method
// engine.onWebSocket(context.req, context.req.socket, context.ws);
}
}
}));
});
挂载思路拆解
Socket.IO 常规用法是 httpServer.listen(),由 node 的 http.Server 同时承担静态资源与升级事件的分发。但 Nuxt 中 HTTP 服务器归 Nitro 所有,无法直接把 socket.io 的 Server 绑到一个独立端口上再暴露 /socket.io/ 路径,因此示例采用了 “无 http 模式 + 手动路由” 的方案:
new Engine()独立创建 engine.io 实例。此时不传入http.Server,engine.io 处于待挂载状态。new Server()+io.bind(engine):socket.io 的Server不直接绑定 http 服务器,而是bind到上一步的 engine 实例上,由 engine 负责底层传输层管理。nitroApp.router.use("/socket.io/", ...):把 Socket.IO 约定的协议路径/socket.io/(含 polling 的 HTTP 请求)注册到 Nitro 的 h3 路由上,请求会被 h3 按前缀匹配并分发。handler(event)处理 HTTP 轮询:将event.node.req / event.node.res交给engine.handleRequest(req, res),对应 engine.io 源码中的 handleRequest 方法,它负责解析?EIO=4&transport=polling之类的 Engine.IO 协议请求、维持轮询会话。随后event._handled = true告知 h3 该响应已由中间层处理完毕,h3 不再兜底。websocket.open(peer)处理 WebSocket 升级:当 Nitro(开启nitro.experimental.websocket后)拦截到/socket.io/?...&transport=websocket的 Upgrade 请求时,调用此回调。回调里先调engine.prepare(req)——对应 engine.io 源码 prepare 方法,负责从升级请求头中解析出会话(sid)并找到对应的 pending 握手;再调engine.onWebSocket(req, socket, ws)——对应 onWebSocket 方法,把底层 WebSocket 对象接入传输层,完成从 polling 到 websocket 的切换。
关于 crossws 版本兼容
peer 对象由 Nitro 依赖的 crossws 库提供。代码中 peer._internal.nodeReq 的取法要求 crossws >= 0.3.0,因此加了 // @ts-expect-error private method and property 注释(访问的是库内部私有成员,源码上并没有类型声明);同时保留了 crossws < 0.3.0 时代的 peer.ctx.node 写法作为注释,供低版本环境参考。这是一个典型的“适配上游库内部 API”的补丁式代码,若未来 Nitro 升级 crossws 大版本,此处是需要重点回归的兼容点。
业务逻辑挂载点
io.on("connection", (socket) => { ... }) 是业务事件的入口,示例中留空(// ...)以突出挂载流程本身。实际项目可在此发送欢迎消息、绑定频道,或结合 socket.io-adapter 实现跨节点广播。
五、客户端:全局 socket 单例与连接状态展示
socket 单例
app/components/socket.ts 只有两行:
import { io } from "socket.io-client";
export const socket = io();
io() 不传任何参数时,socket.io-client 会自动以当前页面同源、同端口为目标建立连接,即直连 Nuxt 开发服务器本身,天然适合全栈示例。模块级导出使整个前端共享同一个 Manager 与 socket 实例,避免每个组件各自握手。
连接状态组件
app/components/Connection.client.vue 展示了连接状态与当前传输层:
<script setup>
import { socket } from "./socket";
const isConnected = ref(false);
const transport = ref("N/A");
if (socket.connected) {
onConnect();
}
function onConnect() {
isConnected.value = true;
transport.value = socket.io.engine.transport.name;
socket.io.engine.on("upgrade", (rawTransport) => {
transport.value = rawTransport.name;
});
}
function onDisconnect() {
isConnected.value = false;
transport.value = "N/A";
}
socket.on("connect", onConnect);
socket.on("disconnect", onDisconnect);
onBeforeUnmount(() => {
socket.off("connect", onConnect);
socket.off("disconnect", onDisconnect);
});
</script>
<template>
<div>
<p>Status: {{ isConnected ? "connected" : "disconnected" }}</p>
<p>Transport: {{ transport }}</p>
</div>
</template>
几个值得注意的实现细节:
.client.vue后缀:Nuxt 约定,以.client结尾的组件只在客户端水合后渲染、不参与 SSR。由于 socket 连接依赖浏览器环境中的 WebSocket,放在纯客户端组件里可以规避 SSR 阶段创建连接的问题;socket.io.engine.transport.name:通过socket.io.engine拿到 engine.io-client 实例,再读取当前传输层名称(polling或websocket),直观呈现 Socket.IO 自动升级的行为;engine.on("upgrade", ...):监听 engine 层的升级事件,在从长轮询切换到 WebSocket 的瞬间刷新Transport显示——这正是第四节 Nitro 侧websocket.open回调生效后的可见结果;onBeforeUnmount中socket.off:组件卸载时解绑 connect/disconnect 监听,防止组件销毁后回调仍在执行。注意这里只移除监听而不socket.close(),符合“单例 socket、多处消费”的共享模式。
app/app.vue 则在模板中渲染 <Connection />,完成从服务端到页面的闭环。
六、生产构建与本地预览
构建生产产物:
# npm
npm run build
# pnpm
pnpm build
# yarn
yarn build
# bun
bun run build
对应 package.json 中的 "build": "nuxt build",由 Nitro 输出服务端 bundle 与静态资源。
本地预览生产构建:
# npm
npm run preview
# pnpm
pnpm preview
# yarn
yarn preview
# bun
bun run preview
对应 "preview": "nuxt preview",用于在本地以接近部署形态的方式验证构建产物,包括 /socket.io/ 路径在 Nitro 生产模式下是否仍正确路由。package.json 中另有 "generate": "nuxt generate" 脚本用于静态站点生成,但对本例这类依赖持久 WebSocket 连接的服务端应用,部署时应以 build + 常驻进程的方式运行 Nitro 产物,具体部署形态建议参考 Nuxt 官方的部署文档(README 中给出的指引)。
七、小结与延伸阅读
本示例的最小集成路径可以概括为三步:
- 配置:
nuxt.config.ts中打开nitro.experimental.websocket; - 服务端:Nitro 插件中创建独立
Engine与Server,io.bind(engine)后经nitroApp.router.use("/socket.io/", ...)把 HTTP 轮询与 WebSocket 升级分别交给engine.handleRequest与engine.prepare + engine.onWebSocket; - 客户端:
io()同源单例 + 客户端专属组件展示连接与传输层状态。
若想继续深入,可以阅读仓库中 engine.io 服务端的 完整实现,理解握手、会话管理与传输层升级的协议细节;也可以对照 chat 示例 与 connection-state-recovery 示例,了解事件广播、断线重连等更进一步的应用模式。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00