vue+node+websocket实现在线聊天

1、下载相关的包
node使用:socket.io
vue使用:vue-socket.io和socket.io-client
2、解决跨域的问题
3、在客户端和服务端注册、监听、触发事件
4、广播实现群组交流

一、配置环境

1、服务端(node)

npm install socket.io --save

  • 创建socket.js文件
let socketio = {}
let socket_io = require('socket.io')

// 获取io
socketio.getSocketio = function(server) {
//这一段是用于解决跨域问题
  let io = socket_io(server, {
    cors: {
      origin: '*'
    }
  })
module.exports = socketio
let http = require('http')
let app = require('../app')
let io = require('../public/socket/mysocket')

let server = http.createServer(app)
//注意要在创建服务之后再使用socket.js中暴露的方法
io.getSocketio(server)
2、客户端(vue)

npm install vue-socket.io --save
npm install socket.io-client --save

  • 在main.js中引入
import VueSocketIO from 'vue-socket.io'
import SocketIO from 'socket.io-client'
Vue.use(new VueSocketIO({
  debug: true,
  // 服务器端地址
  connection: SocketIO('ws://127.0.0.1:3000'),
  // options: { path: '/node' },
  vuex: {
  }
}))
  • 在组件中使用socket
sockets: {
    connect: function() { // 这里是监听connect事件
      console.log('this.$socket.id', this.$socket.id)
    }
}

二、解决跨域问题

1、服务端(在socket.js中加入如下代码)
let socketio = {}
let socket_io = require('socket.io')

// 获取io
socketio.getSocketio = function(server) {
//这一段是用于解决跨域问题
  let io = socket_io(server, {
    cors: {
      origin: '*'
    }
  })
module.exports = socketio
2、客户端(在vue.config.js中加入如下代码)
proxy: {
      //socket.io会默认添加/socket.io后缀
      [process.env.VUE_APP_BASE_API + '/socket.io']: {
        target: `http://127.0.0.1:3000`,
        changeOrigin: true,
        pathRewrite: {
          [`^` + process.env.VUE_APP_BASE_API ]: ''
        }
      }
}
3、浏览器中的效果
  • 浏览器控制台中打印出如下信息就说明已经成功连接
    image.png

    -同时还会输出客户端已经注册的方法
    image.png

三、监听、触发事件

1、服务端
//监听事件
socket.on('send', (data) => {})
//触发事件
socket.emit('send', "触发send")
2、客户端
//监听事件
sockets: {
    send: function(data) {}
}
//触发事件
this.$socket.emit('send', "触发事件")

四、广播实现群组交流

1、服务端
//先加入房间
socket.join('/room1')
//将信息通过broadcastMsg广播给所有已连接的人
io.to('/room1').emit('broadcastMsg', "通知")
2、客户端
//通过监听broadcastMsg方法拿到广播消息
broadcastMsg(data) {
  console.log(data)
}
5、实现在线聊天室
  • 服务端 socket.js文件
/*
封装socket.io,为了获取server以便监听.
 */
let socketio = {}
let socket_io = require('socket.io')

// 获取io
socketio.getSocketio = function(server) {
  let io = socket_io(server, {
    cors: {
      origin: '*'
    }
  })

  io.on('connection', (socket) => {
    socket.on('connSuc', (data) => {
      socket.join('/kf7')
      io.to('/kf7').emit('broadcastMsg', data.name)
    })
    socket.on('send', (data) => {
      io.to('/kf7').emit('send', data)
    })
  })
}

module.exports = socketio
  • 客户端 chat.vue
<template>
  <div class="attendlist_wrap">
    <van-sticky>
      <van-nav-bar :title="$route.meta.title+' ('+txt+') '" />
    </van-sticky>
    <p class="allPeople">当前在线成员: {{ allPeople }}</p>
    <div v-for="(item,index) in msgArr" :key="index">
      <div class="msgCard">
        <p :class="['name',item.type=='recv'?'':'recv']">{{ item.name }}</p>
        <div :class="['con',item.type=='recv'?'':'recv']">
          <p class="msg">{{ item.msg }}</p>
        </div>
      </div>
    </div>
    <div class="botIpt">
      <van-field
        v-model="sendMsg"
        center
        clearable
        placeholder=""
      >
        <template #button>
          <van-button class="sendBtn" size="small" type="primary" @click="sendClick"> 发送 </van-button>
        </template>
      </van-field>
    </div>
    
  </div>
</template>
<script>
import { changePsd, getUserInfo } from '@/api/my'
// import { monthFilter } from '@/utils/session'
import { Toast } from 'vant'
import { setInfo, getInfo } from '@/utils/session'
// import { mapGetters } from 'vuex'
export default {
  data() {
    return {
      connStatus: 0,
      txt: '未连接...',
      allPeople: '',
      sendMsg: '',
      msgArr: [
        // { type: 'send', name: '刘菲', msg: '今天又变仙女了' },
        // { type: 'recv', name: '杨米', msg: '凡尔赛凡尔赛' }
      ]
    }
  },
  sockets: {
    connect: function() { // 这里是监听connect事件
      let info = { 
        socketid: this.$socket.id,
        username: getInfo('username'),
        name: getInfo('nickname'), 
        msg: '连接成功'
      }
      this.txt = '已连接...'
      this.$socket.emit('connSuc', info)
    },
    broadcastMsg(data) {
      this.$notify({
        message: data + '上线了!',
        color: '#07c160',
        background: '#fff',
        duration: 500
      })
    },
    disconnect() {
      this.txt = '未连接...'
      console.log(getInfo('username') + '断开连接')
    },
    send(data) {
      if (data.name == getInfo('nickname')) {
        data.type = 'send'
      } else {
        data.type = 'recv'
      }
      this.msgArr.push(data)
    },
  },
  methods: {
    sendClick() {
      let sendObj = { 
        type: 'send', 
        name: getInfo('nickname'), 
        msg: this.sendMsg
      }
      this.sendMsg = ''
      this.$socket.emit('send', sendObj)
    }
  }
}
</script>
<style lang="scss" scoped>
    .attendlist_wrap{
        width:100%;
        height: 93.2vh;
        margin-bottom: 50px;
        background-color: #fafafa;
        position: relative;
        .allPeople{
          text-align: center;
          margin: 10px auto;
        }
        .msgCard{
          margin: 15px;
          margin-bottom: 0;
          .name{
              margin-bottom: 5px;
              margin-left: 5px;
               &.recv{
                text-align: right;
                margin-right: 5px;
              }
            }
          .con{
            display: flex;
            justify-content: flex-start;
            .msg{
              width: fit-content;
              height: fit-content;
              padding: 10px;
              font-size: 14px;
              border: 1px solid #07c160;
              border-radius: 10px;
              box-sizing: border-box;
            }
            &.recv{
              justify-content: flex-end;
            }
          }
        }
        .botIpt{
          width:100%;
          height:60px;
          position: fixed;
          bottom: 60px;
          left: 0;
          .sendBtn{
            width: 70px;
            letter-spacing: 1px;
          }
        }
    }
</style>

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容