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

NuxtJS+Express+SocketIO+JWT环境下,服务器完全加载后Emit事件停止推送的问题排查

Troubleshooting Socket.IO Integration Issues in Nuxt.js

Let's break down and fix the two main issues you're facing: the hello! undefined log message, and the loss of event reception after Webpack compilation.

1. Fixing hello! undefined

The undefined value here means your decoded JWT token doesn't have a name field in its payload. Let's debug and resolve this:

Step 1: Inspect the decoded token

First, modify your server-side authenticated handler to log the full decoded token structure so you can see what fields are actually present:

.on('authenticated', (socket) => {
  // Log the entire decoded token to check its structure
  console.log('Decoded JWT Token:', socket.decoded_token);
  // Use a fallback field if `name` doesn't exist
  const userName = socket.decoded_token.name || socket.decoded_token.username || socket.decoded_token.sub || 'Guest';
  console.log(`hello! ${userName}`);
})

Step 2: Verify JWT payload

Double-check how you're generating JWT tokens in your auth flow. Ensure the payload includes the field you're trying to access (e.g., name, username, or sub).


2. Fixing Event Reception Loss After Webpack Compilation

This happens because Nuxt's Hot Module Replacement (HMR) in development mode re-renders components, and your current socket setup creates a local connection that gets discarded when the component re-mounts. Here are two solid solutions:

Centralize your socket instance in Vuex to maintain a single connection across component re-renders, and clean up listeners when components unmount.

1. Add a Socket Vuex Module (store/modules/socket.js)

import io from 'socket.io-client';

export const state = () => ({
  instance: null,
  isAuthenticated: false
});

export const mutations = {
  SET_SOCKET(state, socket) {
    state.instance = socket;
  },
  SET_AUTH_STATUS(state, status) {
    state.isAuthenticated = status;
  }
};

export const actions = {
  initSocket({ commit, rootState }) {
    const token = rootState.user.token;
    const socket = io.connect('http://192.168.41.15:3000');

    socket.on('connect', () => {
      socket.emit('authenticate', { token })
        .on('authenticated', () => {
          commit('SET_AUTH_STATUS', true);
          console.log('Socket authenticated successfully');
        })
        .on('unauthorized', (msg) => {
          console.error(`Socket unauthorized: ${JSON.stringify(msg.data)}`);
          commit('SET_AUTH_STATUS', false);
        });
    });

    commit('SET_SOCKET', socket);
    return socket;
  },
  disconnectSocket({ state, commit }) {
    if (state.instance) {
      state.instance.disconnect();
      commit('SET_SOCKET', null);
      commit('SET_AUTH_STATUS', false);
    }
  }
};

2. Update Your test.vue Component

<template>
  <div>
    <h1>Testing Socket.IO</h1>
    <v-text-field v-model="latestTickId" readonly />
    <v-btn @click="sendTestEvent">Send Test</v-btn>
  </div>
</template>

<script>
export default {
  data() {
    return {
      latestTickId: 0,
      tickListener: null,
      testListener: null
    };
  },
  async mounted() {
    // Initialize socket if it doesn't exist
    if (!this.$store.state.socket.instance) {
      await this.$store.dispatch('socket/initSocket');
    }
    const socket = this.$store.state.socket.instance;

    // Save listener references to clean up later
    this.tickListener = (tickId) => {
      this.latestTickId = tickId;
    };
    this.testListener = (data) => {
      console.log('Incoming test data:', data);
    };

    // Attach listeners
    socket.on('tick', this.tickListener);
    socket.on('test', this.testListener);
  },
  beforeDestroy() {
    const socket = this.$store.state.socket.instance;
    if (socket) {
      // Remove listeners to avoid memory leaks
      socket.off('tick', this.tickListener);
      socket.off('test', this.testListener);
      // Disconnect in dev mode to avoid stale connections during HMR
      if (process.env.NODE_ENV === 'development') {
        this.$store.dispatch('socket/disconnectSocket');
      }
    }
  },
  methods: {
    sendTestEvent() {
      const socket = this.$store.state.socket.instance;
      if (socket && this.$store.state.socket.isAuthenticated) {
        socket.emit('test', { id: 'abc123' });
      } else {
        console.error('Socket not initialized or authenticated');
      }
    }
  }
};
</script>

Option B: Use a Nuxt Plugin for Global Socket

Create a client-side plugin to maintain a single socket instance across your app, with HMR support:

1. Create plugins/socket.js

import io from 'socket.io-client';

export default ({ app, store }, inject) => {
  const createSocket = () => {
    const token = store.state.user.token;
    const socket = io.connect('http://192.168.41.15:3000');

    socket.on('connect', () => {
      socket.emit('authenticate', { token })
        .on('authenticated', () => console.log('Socket authenticated'))
        .on('unauthorized', (msg) => console.error(`Socket unauthorized: ${JSON.stringify(msg.data)}`));
    });

    inject('socket', socket);
    return socket;
  };

  // Initialize socket on app load
  let socket = createSocket();

  // Handle HMR in dev mode
  if (process.env.NODE_ENV === 'development' && module.hot) {
    module.hot.accept(() => {
      socket.disconnect();
      socket = createSocket();
    });
  }
};

2. Register the Plugin in nuxt.config.js

export default {
  // ... other config
  plugins: [
    { src: '~/plugins/socket.js', mode: 'client' } // Only run on client side
  ]
};

3. Simplify test.vue

<template>
  <div>
    <h1>Testing Socket.IO</h1>
    <v-text-field v-model="latestTickId" readonly />
    <v-btn @click="sendTestEvent">Send Test</v-btn>
  </div>
</template>

<script>
export default {
  data() {
    return {
      latestTickId: 0,
      tickListener: null,
      testListener: null
    };
  },
  mounted() {
    this.tickListener = (tickId) => this.latestTickId = tickId;
    this.testListener = (data) => console.log('Incoming test data:', data);

    this.$socket.on('tick', this.tickListener);
    this.$socket.on('test', this.testListener);
  },
  beforeDestroy() {
    this.$socket.off('tick', this.tickListener);
    this.$socket.off('test', this.testListener);
  },
  methods: {
    sendTestEvent() {
      this.$socket.emit('test', { id: 'abc123' });
    }
  }
};
</script>

3. Fix Server-Side Socket Event Handling

Your current test event listener is attached to the root io instance instead of authenticated sockets. Move it inside the authenticated handler to ensure only authorized sockets can trigger it:

io.on(
  'connection',
  socketioJwt.authorize({
    secret: process.env.PRIVATE,
    timeout: 15000
  })
)
.on('authenticated', (socket) => {
  console.log('Decoded token:', socket.decoded_token);
  console.log(`hello! ${socket.decoded_token.name || 'Guest'}`);

  // Attach test event listener to authenticated socket only
  socket.on('test', (data) => {
    console.log('Received test data:', data);
    io.emit('test', data); // Broadcast to all clients
    // Or use socket.emit('test', data) to reply only to the sender
  });
});

These changes should resolve both the undefined username issue and the event loss after Webpack compilation.

内容的提问来源于stack exchange,提问作者Selage

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 18:24:06