Vue 3 SPA中SignalR路由切换后断开,求免刷新保持连接方案
问题根源
- 应用启动时就执行了
start(),但此时还未完成登录,token无效导致连接失败,之后即便更新了socket.headers,也没有重新触发连接启动。 - Vue3 SPA中模块是静态导入的,
websocket.js只会初始化一次,登录后其他页面拿到的还是那个之前连接失败的Socket实例,自然显示断开。
解决方案
1. 重构websocket.js,控制连接时机
把自动启动逻辑去掉,改为暴露启动、重启方法,同时做好连接状态管理:
// websocket.js import state from './global.js' import { HubConnectionBuilder } from '@microsoft/signalr' // 初始化连接实例,但不立即启动 const socket = new HubConnectionBuilder() .withUrl(state.socket_addr, { headers: () => ({ // 用函数动态获取最新token,避免静态值的滞后问题 'Authorization': state.token }) }) .build() // 封装重启连接的方法 async function restartConnection() { try { // 如果当前连接处于活跃状态,先断开再重启 if (socket.state === 'Connected') { await socket.stop() } await socket.start() console.log('SignalR 连接成功') } catch (err) { console.error('SignalR 连接失败:', err) setTimeout(restartConnection, 5000) } } // 监听连接断开事件,自动尝试重连 socket.onclose(() => { console.log('SignalR 连接断开,将尝试重连') setTimeout(restartConnection, 5000) }) export { socket, restartConnection }
2. 优化global.js的token响应性
确保登录后token更新能被websocket的动态header函数捕获:
// global.js import { reactive } from 'vue' const state = reactive({ isAuthenticated: !!localStorage.getItem('user_token'), token: 'Bearer ' + (localStorage.getItem('user_token') || '') }) export default state
3. 登录页面触发连接重启
在登录成功后,更新全局token并手动重启SignalR连接:
// Login.vue示例代码 import { restartConnection } from './websocket.js' import state from './global.js' import { useRouter } from 'vue-router' const router = useRouter() // 登录成功后的处理逻辑 async function handleLogin() { // 此处替换为实际登录请求,获取用户token const userToken = '接口返回的用户token' localStorage.setItem('user_token', userToken) // 更新全局状态 state.token = 'Bearer ' + userToken state.isAuthenticated = true // 重启SignalR连接 await restartConnection() // 跳转到列表页 router.push('/lists') }
关键说明
- 用动态header函数替代静态赋值,确保每次通信都能拿到最新的有效token。
- 不在模块加载时自动启动连接,而是放在登录成功后手动触发,避免无效token导致的初始连接失败。
- 监听连接断开事件实现自动重连,确保路由切换、网络波动后连接能自动恢复。
内容的提问来源于stack exchange,提问作者calllpso
相关产品推荐
相关产品推荐

