Nuxt3中Socket.io仅页面刷新时生效问题排查
Socket.io
getApplicationsCountByDay 仅页面刷新触发,路由跳转后失效问题解决 问题现象
在Nuxt3项目集成Socket.io时,后端的getApplicationsCountByDay函数仅在页面刷新时会触发并推送applicationEvent事件;用户离开页面再返回后,该函数不会执行,但其他Socket事件在路由跳转后可正常工作。
相关代码
Nuxt插件代码
import { io } from 'socket.io-client'; export default defineNuxtPlugin((context) => { let socket = null; const { status, data } = useAuth() const getHeaders = () => { const authToken = useCookie('auth:token'); if (authToken.value) { return { Authorization: `Bearer ${authToken.value}`, }; } return {}; }; const createSocketInstance = () => { socket = io('http://localhost:4501', { extraHeaders: getHeaders(), }); return socket; }; const updateSocketHeaders = () => { if (status.value === "authenticated") { socket.io.opts.extraHeaders = getHeaders(); } else { socket.io.opts.extraHeaders = {}; } }; watch(status, (nv, ov) => { if (socket) { updateSocketHeaders(); } }) createSocketInstance(); return { provide: { io: socket, }, }; });
全局中间件代码
export default defineNuxtRouteMiddleware(async (to, from) => { const { $io } = useNuxtApp(); const { status, data } = useAuth(); if (status.value === "authenticated" && process.client) { try { if (!$io.connected) { await $io.connect(); $io.emit('joinRoom',data.value.user.id) } $io.on('error', (error) => { console.error('Socket connection error:', error); }); } catch (error) { console.error('Error connecting to socket:', error); } } });
Express后端代码
io.on('connection', async (socket) => { if (socket.handshake && socket.handshake.auth) { // 仅在Socket连接建立时执行 getApplicationsCountByDay(io,socket.handshake.auth) // 该函数会推送'applicationEvent'事件,仅页面刷新时生效 } });
页面处理代码
<script setup> onMounted(async ()=>{ $io.on('applicationEvent', (receivedData) => { console.log('ok count'); dataValues.value = receivedData.map((item) => item.count); }); }) </script>
核心原因
- Socket连接复用导致
connection事件不触发:Nuxt是单页应用,路由跳转时Socket连接不会断开,全局中间件中仅在!$io.connected时才执行连接逻辑,返回页面时不会触发后端的connection回调,自然不会执行getApplicationsCountByDay。 - 业务逻辑绑定错误:
getApplicationsCountByDay仅绑定在Socket首次连接的connection事件中,没有关联到页面进入的动作(如路由跳转、房间加入)。 - 页面监听虽会重新绑定,但后端无事件推送:路由返回时页面
onMounted会重新绑定applicationEvent监听,但后端没有触发该事件的逻辑,所以收不到数据。
解决方案
方案1:页面主动请求数据(推荐)
让页面在挂载时主动向后端发送请求,触发统计数据推送,无需依赖Socket连接事件。
- 修改页面代码,新增主动请求逻辑并清理监听:
<script setup> onMounted(async () => { // 主动请求后端发送统计数据 $io.emit('requestApplicationsCount'); // 定义事件处理函数,方便后续移除监听 const handleApplicationEvent = (receivedData) => { console.log('ok count'); dataValues.value = receivedData.map(item => item.count); }; $io.on('applicationEvent', handleApplicationEvent); // 页面卸载时移除监听,避免内存泄漏 onUnmounted(() => { $io.off('applicationEvent', handleApplicationEvent); }); }) </script>
- 修改后端代码,新增
requestApplicationsCount事件监听:
io.on('connection', async (socket) => { if (socket.handshake && socket.handshake.auth) { // 保留页面刷新时的触发逻辑(可选) getApplicationsCountByDay(io, socket.handshake.auth); // 新增主动请求的事件监听 socket.on('requestApplicationsCount', () => { getApplicationsCountByDay(io, socket.handshake.auth); }); } });
方案2:绑定逻辑到joinRoom事件
利用现有joinRoom事件,在用户加入房间时触发统计数据推送。
- 修改后端代码,将
getApplicationsCountByDay绑定到joinRoom:
io.on('connection', async (socket) => { if (socket.handshake && socket.handshake.auth) { // 监听joinRoom事件,用户每次进入页面都会触发 socket.on('joinRoom', (userId) => { getApplicationsCountByDay(io, socket.handshake.auth); }); } });
- 修改全局中间件,确保每次进入页面都发送
joinRoom:
export default defineNuxtRouteMiddleware(async (to, from) => { const { $io } = useNuxtApp(); const { status, data } = useAuth(); if (status.value === "authenticated" && process.client) { try { if (!$io.connected) { await $io.connect(); } // 不管是否已连接,都发送joinRoom事件 $io.emit('joinRoom', data.value.user.id); $io.on('error', (error) => { console.error('Socket connection error:', error); }); } catch (error) { console.error('Error connecting to socket:', error); } } });
方案3:强制重新连接Socket(不推荐)
通过断开再重连Socket,触发后端connection事件,但会影响性能,仅用于临时测试。
修改全局中间件:
export default defineNuxtRouteMiddleware(async (to, from) => { const { $io } = useNuxtApp(); const { status, data } = useAuth(); if (status.value === "authenticated" && process.client) { try { // 先断开现有连接 if ($io.connected) { await $io.disconnect(); } // 重新连接触发connection事件 await $io.connect(); $io.emit('joinRoom', data.value.user.id); $io.on('error', (error) => { console.error('Socket connection error:', error); }); } catch (error) { console.error('Error connecting to socket:', error); } } });
内容的提问来源于stack exchange,提问作者Joe_G
相关产品推荐
相关产品推荐

