vue使用 vue-socket.io三种方式及踩坑实例解析

目录
  • 前言
  • 安装
  • 使用方式一 (官方用法)[全局挂载,不验证]
  • 使用方式二 (组件挂载使用)[可验证]
  • 使用方式三 (全局挂载使用)[可验证]
  • 使用方式推荐
  • nodejs服务端本地demo代码
  • 总结

前言

vue项目实时通信实现常用方式:

一、原生HTML5 WebSocket实现,vue中使用websocket

二、插件socket.io官网 ,Socket.io是一个WebSocket库,包括了客户端js和服务器端的nodejs,会自动根据浏览器从WebSocket、AJAX长轮询、Iframe流等等各种方式中选择最佳的方式来实现网络实时应用,最低支持IE5.5; 说白了就是如果浏览器支持websocket,那么socket.io就等同于websocket。当然 socket.io还用到了其它的技术来模拟websocket,所以当你使用socket.io的时候,不管浏览器是否支持websocket,你都可以实现异步操作。

使用注意:客户端使用了socket.io 服务端也必须对应使用

三、vue-socket.io是vue对socket.io的封装,使用方式与socket.io大同小异,核心参数还是要参照socket.io官网相关配置;当前在使用中也存在部分坑

当前文章主要说vue-socket.io以下问题:

  • 初始化全局挂载使用,事件订阅与销毁(基本无坑,需注意订阅事件要对应销毁,否则存在多次订阅引发的消息重复接收问题)
  • 带token验证的全局挂载方式(有坑,有解决方案与解决后使用注意事项)
  • 组件内使用

安装

建议直接使用taobao镜像,淘宝镜像地址建议使用新地址,老地域名将于2022年05月31日零时起停止服务nrm use taobao || npm config set registry registry.npmmirror.com/

npm i vue-socket.io@3.0.10 -S  //个人使用了当前版本

使用方式一 (官方用法)[全局挂载,不验证]

需注意:事件只有在订阅后才可接接收返回消息(订阅方法与后端约定名称)。

实例创建后,全局挂载实例方法,this.$socket,this.sockets。

其中事件订阅、取消与监听方法在this.sockets封装;

this.$socket是socket.io实例中io的封装。

// 全局挂载
// main.js
import VueSocketIO from 'vue-socket.io'
Vue.use(new VueSocketIO({
  debug: true,// 生产环境关闭,打开可在控制台查看socket连接和事件监听的信息
  options: {
    autoConnect: false //创建时是否自动连接,默认关闭,使用时用open开启链接
  },
  connection: 'http://127.0.0.1:9527' //链接地址
}))
//1.全局挂载-全局使用
//main.js
new Vue({
    sockets: {
        connecting() { console.log('正在连接') },
        connect() { console.log('连接成功') },
        disconnect() { console.log('断开连接') },
        connect_failed() { console.log('连接失败') },
        error() { console.log('错误发生,并且无法被其他事件类型所处理') },
        reconnecting() { console.log('正在重连') },
        reconnect_failed() { console.log('重连失败') },
        reconnect() { console.log('重连成功') },
        welcome: data => {//全局监听订阅事件,需要与后端约定好
            console.log('welcome data', data)
        }
    }
})
//2. 全局挂载-组件内使用
//demo.vue
<template>
  <div>
    <button @click="socketOpen">连接Socket</button>
    <button @click="closeSocket">断开链接</button>
    <button @click="submsgContent(true)">订阅事件</button>
    <button @click="submsgContent(false)">取消订阅事件</button>
    <button @click="socketSendmsg">发送数据</button>
    <button @click="lockResult">查看链接参数</button>
  </div>
</template>
<script>
export default {
  beforeDestroy() { //订阅事件记得要取消---否则多次订阅会引发多次消息返回
    if (!this.$socket) return
    this.sockets.unsubscribe('msgContent')
    this.$socket.close()
  },
  sockets: { //监听用的是this.sockets   发送消息是this.$socket,不要弄混
        connecting() { console.log('正在连接') },
        connect() { console.log('连接成功') },
        disconnect() { console.log('断开连接') },
        connect_failed() { console.log('连接失败') },
        error() { console.log('错误发生,并且无法被其他事件类型所处理') },
        reconnecting() { console.log('正在重连') },
        reconnect_failed() { console.log('重连失败') },
        reconnect() { console.log('重连成功') },
        welcome: data => {//全局监听订阅事件,需要与后端约定好
            console.log('welcome data', data)
        }
  },
  methods: {
    socketOpen() {
      this.$socket.open()// 开始连接 socket
    },
    socketSendmsg() { // 发送消息
      this.$socket.emit('hello', '这里是客户端')
    },
    lockResult() {
      console.log('链接状态', this.$socket.connected)
      console.log('this.$socket', this.$socket)
      console.log('this.sockets', this.sockets)
    },
    closeSocket() {
      this.$socket.close()
    },
    submsgContent(flag) {
        if (flag) { //事件订阅
          this.sockets.subscribe('welcome', data => { //组件内监听
            console.log('组件内监听-welcome', data)
          })
        } else {//取消订阅
          this.sockets.unsubscribe('welcome')
        }
    }
  }
}
</script>

使用方式二 (组件挂载使用)[可验证]

因在组件内使用,所以可将逻辑写在登录后加载挂载组件,全局或指定页面使用。

组件内创建注意事项:因vue-socket.io,在new.use()时挂载方法$socket到全局,在创建实例时挂载sockets监听对象到当前实例,所以,在组件中使用后会导致以下问题:

问题:
 1. $socket可正常使用,与$socket也就是io相关的方法都可正常使用(例如:this.$socket.open()、close等等...)
 2. sockets相关方法无法使用(例如:this.sockets.unsubscribe、this.sockets.subscribe等等)
处理方式:使用socket.io原生方法,
 1. 事件订阅 
    实例.emitter.addListener
 2. 取消订阅
    实例.emitter.addListener

 //demo.vue
 <template>
  <div>
    <button @click="createSocket">创建socket</button>
    <div v-if="createSocketIO ? true:false ">
      <button @click="socketOpen">连接Socket</button>
      <button @click="closeSocket">断开链接</button>
      <button @click="submsgContent(true)">订阅事件</button>
      <button @click="submsgContent(false)">取消订阅事件</button>
      <button @click="socketSendmsg">发送数据</button>
      <button @click="lockResult">查看链接参数</button>
    </div>
  </div>
</template>
<script>
import VueSocketIO from 'vue-socket.io'
export default {
  data() {
    return {
      createSocketIO: null,
      createSocketEmitter: null
    }
  },
  beforeDestroy() { //订阅事件记得要取消
    if (this.createSocketIO) {
      this.createSocketEmitter.removeListener('msgContent', this)
      this.createSocketIO.close()
    }
  },
  methods: {
   async createSocket() {//也可在页面初始话调用
      let socketUrl
      /* // 动态ip与token实现,ip+端口可通过接口拿取,token及用户信息可通过vuex拿取
            const userId = store.getters.userInfo.userId
            const ipResult = await getHostIp()
            if (ipResult.code !== 0) return
            const { ip, port } = ipResult.data
            const protocol = window.location.protocol
            const socketUrl = `${protocol}//${ip}:${port}?userId=${userId}`
            console.log('socketUrl', socketUrl)
       */
      socketUrl = 'http://127.0.0.1:9527'
      const createSocketItem = new VueSocketIO({
        debug: true,
        options: {
          autoConnect: false,//默认关闭,创建后打开,组件内使用可直接打开,就不需要用io.open()
          transports: ['websocket'],
          query: {
              token: 77777777777 //携带的额外参数也可通过url拼接实现
          }
        },
        connection:socketUrl
      })
      const { io, emitter } = createSocketItem
      io.query.ttt = 8888888888
      this.createSocketIO = io
      this.createSocketEmitter = emitter
      io.open()
      io.on('connecting', () => { console.log('正在连接---888') })
      io.on('connect', () => { console.log('连接成功---888') })
      io.on('disconnect', () => { console.log('断开连接---888') })
      io.on('connect_failed', () => { console.log('连接失败---888') })
      io.on('error', () => { console.log('错误发生,并且无法被其他事件类型所处理') })
      io.on('reconnect_attempt', () => {console.log('触发尝试重新连接', 888)})
      io.on('reconnecting', () => { console.log('正在重连---888') })
      io.on('reconnect_failed', () => { console.log('重连失败---888') })
      io.on('reconnect', () => { console.log('重连成功---888') })
      emitter.addListener('welcome', (data) => {
        console.log('data', data)
      }, this)
    },
    socketOpen() {
      this.createSocketIO.open()
    },
    socketSendmsg() { // 发送消息
      this.createSocketIO.emit('hello', '这里是客户端')
    },
    lockResult() {
      console.log('链接状态', this.createSocketIO.connected)
    },
    closeSocket() {
      this.createSocketIO.close()
    },
    submsgContent(flag) {
      if (flag) {
        this.createSocketEmitter.addListener('welcome', (data) => {
          console.log('data', data)
        }, this)
      } else {
        this.createSocketEmitter.removeListener('welcome', this)
      }
    }
  }
}
</script>

使用方式三 (全局挂载使用)[可验证]

当前方法实现类似直接使用socket.io,基本相当于只是使用了vue-socket.io的库与全局挂载的this.$scoket方法。

注: vue.use( new VueSocketIO({connection:'http://127.0.0.1:9527'})) 只将实例中io模块挂载了this.$scoket

实现思路与遭遇问题:

实现:

通过本地轮询,实时获取用户登录状态,用户登录后挂载全局方法

问题:

同实现方式二问题,vue-socket.io,在new.use()时挂载方法$socket到全局(io模块),在创建实例时挂载sockets监听对象到当前实例;在轮询拿到用户当前挂载相关方法时,当前项目实例已经被创建,能拿到this.$socket,不能拿到this.sockets;需要使用页面后置加载,或重新加载后正常,挂载方法才能正常使用

解决方式:

在创建实例时,将除io,模块的事件(emitter)与监听(listener)也挂载到全局,使用相关定义方法在mounted生命周期中进行;登录前尽量把数据存储好后在进入系统

使用示例:

//main.js 入口文件全局挂载
// import Vue from 'vue'
import router from '@/router'
import store from '@/store'
import App from './App'
import '@/util/socket.js'  //使用全局挂载
Vue.config.productionTip = false
const saasVue = new Vue({
  router,
  store,
  i18n,
  render: h => h(App)
}).$mount('#app')
window.saasVue = saasVue
// socket.js 全局方法实现
import Vue from 'vue'
import VueSocketIO from 'vue-socket.io'
import store from '@/store'
import { getHostIp } from '@/api/admin/message' //获取后端动态ip地址
let socketIo, socketListener, socketEmitter
if (!getToken()) {
  const timer = setInterval(() => {
    console.count()
    if (!getToken()) return
    initSocket(getToken())
    window.clearInterval(timer)
  }, 1000)
} else {
  initSocket(getToken())
}
function getToken() {//获取登录标识---请修改为自己项目标识
  return store.getters.access_token
}
async function initSocket(token) {
  const userId = store.getters.userInfo.userId //获取登录标识---请修改为自己项目用户id
  const ipResult = await getHostIp()
  if (ipResult.code !== 0) return
  const { ip, port } = ipResult.data
  const protocol = window.location.protocol
  const socketUrl = `${protocol}//${ip}:${port}?userId=${userId}`
  console.log('socketUrl', socketUrl)
  //const socketUrl = 'http://127.0.0.1:9527' //本地测试地址---nodejs服务代码放后面
  const socket = new VueSocketIO({
    debug: process.env.NODE_ENV !== 'production',
    options: {
      autoConnect: false //已通过验证,全局使用可默认打开,组件内使用则默认关闭,使用时在打开
    },
    connection: socketUrl
  })
  const { emitter, io, listener } = socket
  socketIo = io
  socketListener = listener
  socketEmitter = emitter
  if (process.env.NODE_ENV !== 'production') {// 与socket链接相关全局处理,与后端预定定义事件在组件内订阅使用
    io.on('connecting', () => { console.log('socketjs---正在连接') })
    io.on('connect', () => { console.log('socketjs---连接成功') })
    io.on('disconnect', () => { console.log('socketjs---断开连接') })
    io.on('connect_failed', () => { console.log('socketjs---连接失败') })
    io.on('error', () => { console.log('socketjs---错误发生,并且无法被其他事件类型所处理') })
    io.on('reconnect_attempt', () => { console.log('socketjs---触发尝试重新连接')})
    io.on('reconnecting', () => { console.log('socketjs---正在重连') })
    io.on('reconnect_failed', () => { console.log('socketjs---重连失败') })
    io.on('reconnect', () => { console.log('socketjs---重连成功') })
  }
  //   Vue.use(socket) //只是挂载了io模块,存在已加载页面无法订阅问题,不如不使用,自己挂载方便
  Object.defineProperty(Vue.prototype, '$socketIo', { value: socketIo })
  Object.defineProperty(Vue.prototype, '$socketListener', { value: socketListener })
  Object.defineProperty(Vue.prototype, '$socketEmitter', { value: socketEmitter })
}
export default { socketIo, socketListener, socketEmitter }

组件内使用:

//demo.vue  订阅事件与销毁在组件中进行
<template>
  <div>
    <button @click="socketOpen">连接Socket</button>
    <button @click="closeSocket">断开链接</button>
    <button @click="submsgContent(true)">订阅事件</button>
    <button @click="submsgContent(false)">取消订阅事件</button>
    <button @click="socketSendmsg">发送数据</button>
    <button @click="lockResult">查看链接参数</button>
  </div>
</template>
<script>
export default {
  beforeDestroy() { //订阅事件记得要取消
    if (this.$socketIo) {
      this.$socketEmitter.removeListener('msgContent', this)
      this.$socketIo.close()
    }
  },
  mounted() {
    this.$socketIo.open()//初始化打开链接
    this.$socketEmitter.addListener('welcome', (data) => {//组件初始化挂载后,订阅后端事件
      console.log('data', data)
    }, this)
  },
  methods: {
    socketOpen() {
      this.$socketIo.open()
    },
    socketSendmsg() { // 发送消息
      this.$socketIo.emit('hello', '这里是客户端')
    },
    lockResult() {
      console.log('链接状态', this.$socketIo.connected)
    },
    closeSocket() {
      this.$socketIo.close()
    },
    submsgContent(flag) {
      if (flag) {
        this.$socketEmitter.addListener('welcome', (data) => {
          console.log('data', data)
        }, this)
      } else {
        this.$socketEmitter.removeListener('welcome', this)
      }
    }
  }
}
</script>

使用方式推荐

方式一:适用于全局通知类业务类型多,不验证用户登录,一般是门口网站引流类型使用。

方法二:适用于通知类型较少的业务项目

方式三:适用项目通知类型多,业务场景模块较多情况,全局挂载,每个页面只需要做对应方法订阅,与销毁;例如:系统消息+业务消息待办通知,首屏监控、大屏展示下多个模块多订阅场景。

nodejs服务端本地demo代码

相关依赖:

npm install socket.io@2.0.4 -S
// nodeSocket.js
var http = require('http')
var io = require('socket.io')
// 创建server服务
var server = http.createServer(function(req, res) {
  var headers = {}
  headers['Access-Control-Allow-Origin'] = '*'
  headers['Access-Control-Allow-Methods'] = 'POST, GET, PUT, DELETE, OPTIONS'
  headers['Access-Control-Allow-Credentials'] = true
  headers['Access-Control-Max-Age'] = '86400' // 24 hours
  headers['Access-Control-Allow-Headers'] = 'X-Requested-With, Access-Control-Allow-Origin, X-HTTP-Method-Override, Content-Type, Authorization, Accept'
  res.writeHead(200, headers)
  res.end()
})
// 启动服务器  监听 1024 端口
server.listen(1024, function() {
  console.log('server runing at 127.0.0.1:1024')
})
// 启动socket服务
var socket = io.listen(server, { origins: '*:*' })
socket.use((socket, next) => {
  const query = socket.handshake.query
  console.log('token', query.token)
  if (query.token) {
    return next()
  }
  return next(new Error('authentication error 鉴权失败'))
})
// 监听客户端连接
socket.on('connection', function(socket) {
  console.log('客户端有连接')
  // 监听客户端断开
  socket.on('disconnect', () => {
    console.log('客户端断开')
  })
  // 给客户端发送消息
  setInterval(() => {
    socket.emit('welcome', '欢迎连接socket')
    console.count(1)
  }, 2000)
  // 监听客户端消息
  socket.on('hello', data => {
    console.log('接收客户端数据---:', data)
  })
})

启动:

node nodeSocket.js

总结

  • vue-socket.io使用版本注意,3.0.8、3.0.9有部分bug,感兴趣的可以去试试;
  • nodejs模块使用的socket.io版本注意,socket.io版本大于了2.0.4后使用方法有变化;
  • 消息订阅同一方法可多次订阅,订阅几次,后端一次返回了几次订阅消息。所以注意取消订阅方法。
  • 同一订阅方法,在不同页面使用,销毁后会取消所有使用当前方法的订阅消息。需要不同模块与后端约定不同方法,根据项目消息架构处理;

以上就是vue使用 vue-socket.io三种方式及采坑记录实例解析的详细内容,更多关于vue使用vue-socket.io采坑的资料请关注我们其它相关文章!

(0)

相关推荐

  • vue-socket.io接收不到数据问题的解决方法

    最近公司的一个vue项目用到了vue-socket.io来处理socket数据传输,之前用过socket.io-client,现在知道vue-socket.io是基于socket.io-client的一层封装,将socket挂于全局从而更方便的书写. 于是把代码拉取下来运行: 什么鬼,同样的代码为什么我的就接收不到数据,自己新建一个测试一下吧! 先用express和socket.io搭个小socket服务器: let express = require('express'); let app =

  • 关于vue-socket.io使用及版本原因消息无法监听bug

    目录 前言 一.vue-socket.io使用 1.npm下载 2.在src文件夹里面新建lib文件夹 3.在main.js引入 4.在组件中的用法 二.版本导致消息无法监听问题 在3.0.8和3.0.9下这样写无效 3.0.8和3.0.9要像下面一样写 利用listener来监听 总结 前言 主要记录vue-socket.io使用,以及使用版本不同导致消息无法监听问题. 一.vue-socket.io使用 1.npm下载 npm install vue-socket.io --save-dev

  • 详解vue-socket.io使用教程与踩坑记录

    目录 前言 我遇到的问题 使用教程 安装 引入(main.js) 使用(Page.vue) 解决方案 结合connect事件+store+路由守卫实现拦截 请先允许我狠狠吐个槽:vue-socket.io相关中文博客实在太少太少,来来去去就那么几篇,教程也比较零散,版本也比较老,就算我有暴风式搜索还是找不到解决问题的方案,然后我怒了,开始看源码.写测试demo.几乎把相关的issues都看了一遍,折腾1天后终于...搞定了,下面总结一下~ 考虑到很多小伙伴看完文章还是一头雾水或者无法复现方案,附

  • vue使用 vue-socket.io三种方式及踩坑实例解析

    目录 前言 安装 使用方式一 (官方用法)[全局挂载,不验证] 使用方式二 (组件挂载使用)[可验证] 使用方式三 (全局挂载使用)[可验证] 使用方式推荐 nodejs服务端本地demo代码 总结 前言 vue项目实时通信实现常用方式: 一.原生HTML5 WebSocket实现,vue中使用websocket 二.插件socket.io官网 ,Socket.io是一个WebSocket库,包括了客户端js和服务器端的nodejs,会自动根据浏览器从WebSocket.AJAX长轮询.Ifra

  • vue项目刷新当前页面的三种方式(重载当前页面数据)

    目录 vue项目刷新当前页面的三种方式(重载当前页面数据) 一.this.$router.go(0) 二.location.reload() 三.用provide / inject 组合 PS:vue项目刷新当前页面的三种方法 vue项目刷新当前页面的三种方式(重载当前页面数据) 一.this.$router.go(0) 相当于F5刷新,这种方法虽然代码很少,只有一行,但是体验很差.页面会一瞬间的白屏,体验不是很好 二.location.reload() 这种也是一样,画面一闪,体验不是很好,相

  • vue组件传值的实现方式小结【三种方式】

    本文实例讲述了vue组件传值的实现方式.分享给大家供大家参考,具体如下: 前言 vue的组件传值分为三种方式:父传子.子传父.非父子组件传值 引用官网的一句话:父子组件的关系可以总结为 prop 向下传递,事件向上传递 父组件通过 prop 给子组件下发数据,子组件通过事件给父组件发送消息,如下图所示: 下面我们就开始用代码(一言不合就上代码)详细的介绍vue组件传值的三种方式 1.父传子 子组件的代码: <template> <div id="container"&

  • vue路由传参的基本实现方式小结【三种方式】

    本文实例讲述了vue路由传参的基本实现方式.分享给大家供大家参考,具体如下: 前言 vue 路由传参的使用场景一般都是应用在父路由跳转到子路由时,携带参数跳转.传参方式可划分为 params 传参和 query 传参,而 params 传参又可分为在 url 中显示参数和不显示参数两种方式,这就是vue路由传参的三种方式. 方式一:params 传参(显示参数) params 传参(显示参数)又可分为 声明式 和 编程式 两种方式 1.声明式 router-link 该方式是通过 router-

  • Spring循环依赖的三种方式(推荐)

    引言:循环依赖就是N个类中循环嵌套引用,如果在日常开发中我们用new 对象的方式发生这种循环依赖的话程序会在运行时一直循环调用,直至内存溢出报错.下面说一下spring是如果解决循环依赖的. 第一种:构造器参数循环依赖 Spring容器会将每一个正在创建的Bean 标识符放在一个"当前创建Bean池"中,Bean标识符在创建过程中将一直保持 在这个池中,因此如果在创建Bean过程中发现自己已经在"当前创建Bean池"里时将抛出 BeanCurrentlyInCrea

  • 详解三种方式解决vue中v-html元素中标签样式

    Vue为v-html中标签添加CSS样式 <template> <div class="hello"> <section> <h2 class="title">{{news.title}}</h2> <p class="news-time">{{news.datetime}}</p> <div class="con" v-html=&qu

  • 使用Vue实现图片上传的三种方式

    项目中需要上传图片可谓是经常遇到的需求,本文将介绍 3 种不同的图片上传方式,在这总结分享一下,有什么建议或者意见,请大家踊跃提出来. 没有业务场景的功能都是耍流氓,那么我们先来模拟一个需要实现的业务场景.假设我们要做一个后台系统添加商品的页面,有一些商品名称.信息等字段,还有需要上传商品轮播图的需求. 我们就以Vue.Element-ui,封装组件为例子聊聊如何实现这个功能.其他框架或者不用框架实现的思路都差不多,本文主要聊聊实现思路. 1.云储存 常见的 七牛云,OSS(阿里云)等,这些云平

  • Vue 实现点击空白处隐藏某节点的三种方式(指令、普通、遮罩)

    在项目中往往会有这样的需求: 弹出框(或Popover)在 show 后,点击空白处可以将其 hide. 针对此需求,整理了三种实现方式,大家按实际情况选择. 当然,我们做项目肯定会用到 UI 框架,常见的 Element 中的组件提供了这样的方法. 但是,就算使用框架,有些时候还是要用到的,比如: Element 中的 Popover,当我们想使用手动方式(trigger 触发方式为 manual时)控制它的 show & hide 的时候,就要自己实现这个功能啦. 第一种方式:最普通的手动监

  • vue请求数据的三种方式

    请求数据的方式: vue-resource 官方提供的 vue的一个插件 axios fetch-jsonp 一,vue-resource请求数据 介绍:vue-resource请求数据方式是官方提供的一个插件 使用步骤: 1.安装vue-resource模块 cnpm install vue-resource --save 加--save是为了在package.json中引用,表示在生产环境中使用.因为我们在日常开发中,如果我们要打包代码给其他人或者上传到github,又或者要发布代码时,pa

  • 前端vue中文件下载的三种方式汇总

    目录 前端vue中文件下载的三种方式 附:vue实现图片或文件下载功能实例 总结 前端vue中文件下载的三种方式 第一种方式是前端创建超链接,通过a标签的链接向后端服务发get请求,接收后端的文件流,非常简单: <a :href='"/user/downloadExcel"' >下载模板</a> 另一种情况是创建div标签,动态创建a标签: <div name="downloadfile" onclick="downloadE

随机推荐