Vue事件总线传递Socket.io对象后无法访问属性如何解决?
解决Vue事件总线传递Socket.io对象后无法访问
id属性的问题 问题根源
你碰到的这个问题我之前也踩过坑,核心原因是Socket.io的连接是异步操作:当你在主实例里调用socket.open()后,并不会立刻完成和服务器的握手,此时socket对象还没生成id(这个属性是服务器成功握手后才会赋值的)。你紧接着通过事件总线传递的socket,其实还处于「正在连接」的状态,组件自然拿不到有效的id。
具体解决方案
我们需要等Socket.io真正完成连接、id属性就绪后,再把socket传递给组件,具体修改如下:
1. 修改主Vue实例代码
把主实例里的逻辑调整为:先监听Socket.io自身的connect事件,确认连接成功后再通过事件总线通知组件:
import Vue from 'vue' import { EventBus } from './event-bus' import App from './App' import router from './router' import SocketIO from 'socket.io-client' const socket = SocketIO('https://host-notification.herokuapp.com/', { autoConnect: false }); new Vue({ el: '#inbox', router, render: h => h(App), watch: { $route(to, from ){ console.log(to, from); } }, created() { this.$router.push({ path: 'inbox' }); // 先监听socket的connect事件,确保连接就绪后再通知组件 socket.on('connect', () => { console.log('Socket真正连接成功,id为:', socket.id); EventBus.$emit('connected', socket); }); }, mounted() { console.log('mounted'); EventBus.$on('open-connection', (data) => { console.log('触发连接请求'); socket.open(); }) } })
2. 优化组件代码
组件里接收socket后,不仅能直接访问id,还可以监听socket的断开事件维护状态,同时记得在组件销毁时移除事件监听避免内存泄漏:
<script> import { EventBus } from '../../event-bus'; export default { data () { return { socket: null, isRegistered: false, isConnected: false, username: null, message: '', } }, mounted() { const connectedHandler = (socket) => { this.socket = socket; this.isConnected = true; // 此时socket已就绪,可正常访问id console.log('组件拿到的socket id:', this.socket.id); // 可选:监听断开事件,更新状态 this.socket.on('disconnect', () => { this.isConnected = false; console.log('Socket连接断开'); }); }; EventBus.$on('connected', connectedHandler); // 存下监听函数,方便销毁时移除 this.connectedHandler = connectedHandler; }, beforeDestroy() { // 组件销毁时移除事件总线监听,防止内存泄漏 EventBus.$off('connected', this.connectedHandler); if (this.socket) { // 可选:组件销毁时断开socket连接 // this.socket.disconnect(); } }, methods: { connect(){ if( !this.isRegistered ){ this.isRegistered = true; EventBus.$emit('open-connection'); return this.username; } } } } </script>
额外优化建议
如果你的项目有多个组件需要用到socket,其实可以直接把socket挂载到Vue原型上,省去事件总线传递的麻烦:
// 主实例中 Vue.prototype.$socket = socket; // 组件中直接使用 this.$socket.on('connect', () => { console.log(this.$socket.id); });
内容的提问来源于stack exchange,提问作者guggio
相关产品推荐
相关产品推荐

