You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>

核心原因

  1. Socket连接复用导致connection事件不触发:Nuxt是单页应用,路由跳转时Socket连接不会断开,全局中间件中仅在!$io.connected时才执行连接逻辑,返回页面时不会触发后端的connection回调,自然不会执行getApplicationsCountByDay。
  2. 业务逻辑绑定错误:getApplicationsCountByDay仅绑定在Socket首次连接的connection事件中,没有关联到页面进入的动作(如路由跳转、房间加入)。
  3. 页面监听虽会重新绑定,但后端无事件推送:路由返回时页面onMounted会重新绑定applicationEvent监听,但后端没有触发该事件的逻辑,所以收不到数据。

解决方案

方案1:页面主动请求数据(推荐)

让页面在挂载时主动向后端发送请求,触发统计数据推送,无需依赖Socket连接事件。

  1. 修改页面代码,新增主动请求逻辑并清理监听:
<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>
  1. 修改后端代码,新增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事件,在用户加入房间时触发统计数据推送。

  1. 修改后端代码,将getApplicationsCountByDay绑定到joinRoom:
io.on('connection', async (socket) => {
  if (socket.handshake && socket.handshake.auth) {
    // 监听joinRoom事件,用户每次进入页面都会触发
    socket.on('joinRoom', (userId) => {
      getApplicationsCountByDay(io, socket.handshake.auth);
    });
  }
});
  1. 修改全局中间件,确保每次进入页面都发送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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 21:27:10