express如何使用websocket进行实时通讯

效果图: 

1、首先安装依赖

npm install express
npm install express-ws

2、在index.js中引用

import express from "express";
import expressWs from "express-ws";

这边我是用的es6模块化导入语法,可以在package.json中设置"type": "module"

3、使用expressWs

const app = express()
expressWs(app);
// 每个在线用户所发送的消息
const wss = expressWs(app).getWss('/')

4、发起请求

app.ws('/socket', (ws, req) => {
  console.log('连接成功');
  try {
    // 监听消息事件
    ws.on('message', msg => {
      console.log('serve message', msg);
      let msgObj = JSON.parse(msg)
      let sql = 'insert into chat_messages set ?'
      wss.clients.forEach((e) => {
        msgObj.date = moment(new Date()).format('YYYY-MM-DD HH:mm:ss')
        e.send(JSON.stringify(msgObj))
      })
      db.query(sql, JSON.parse(msg), (err, data) => {
        if (err) return ws.send('sql出错了' + err.message);
        if (data.affectedRows !== 1) return res.send({
          code: 2,
          message: '用户信息保存数据库失败'
        })
      })
    })
    ws.on('open', () => {
      console.log('serve open');
    })
    ws.on('error', () => {
      console.log('serve error');
    })
    ws.on('close', () => {
      console.log('serve close');
    })
  } catch (error) {
    console.log(error);
  }
});
app.listen(3000, () => {
  console.log('运行于http://127.0.0.1:3000');
})

不存数据库的话这部可以不要

db.query(sql, JSON.parse(msg), (err, data) => {
   if (err) return ws.send('sql出错了' + err.message);
   if (data.affectedRows !== 1) return res.send({
     code: 2,
     message: '用户信息保存数据库失败'
   })
 })

5、前端vue代码

<template>
  <div>
    <div ref="messagesContainer" :style="{ width: '450px',height: contentHeight + 'px',border: '1px solid #ccc', marginBottom: '16px',overflow: 'auto',padding: '10px' }">
      <!-- <p style="text-align: center;font-size: 14px">您正在和{{ username }}聊天</p> -->
      <div style="width: 100%" v-for="(item, index) in messageList" :key="index">
        <div style="width: 300px;margin-bottom: 10px;" :class="item.userid === userid ? 'right' : ''">
          <h3 style="font-size: 14px;margin-bottom: 5px;" :class="item.userid === userid ? 'title_right' : ''">{{ item.username }} {{ moment(item.create_time).format('YYYY-MM-DD HH:mm:ss') }}</h3>
          <div style="width: 100%;">
            <div :class="item.userid === userid ? 'float_left' : 'float_right'" style="width: 250px;min-height: 40px;background-color: #e8e8e9;padding: 10px;white-space: wrap;border-radius: 10px;">{{ item.message }}</div>
            <div :class="item.userid === userid ? 'float_left' : 'float_right'" style="width: 40px;height: 40px;">
              <img :src="item.imgUrl" alt="" style="width: 100%;height: 100%;object-fit: cover;">
            </div>
            <div style="clear: both;"></div>
          </div>
        </div>
        <div style="clear: both;"></div>
      </div>
    </div>
    <Input v-model="message" placeholder="请输入内容" style="width: 280px" @on-enter="sendMessage"/>
    <Button type="success" @click="sendMessage" style="margin: 10px;">发送</Button>
    <Button type="success" @click="clear">清空记录</Button>
  </div>
</template>
<script setup>
import moment from 'moment';
import { onMounted, reactive, ref, nextTick, onUpdated } from 'vue';
import { Message } from 'view-ui-plus'
import { getMessageListApi } from '../../utils/message.js'
let message = ref('')
let messageList = reactive([])
let messagesContainer = ref(null)
let socket = new WebSocket('http://127.0.0.1:3000/socket')
let username = JSON.parse(localStorage.getItem('userinfo')).nickname
let imgUrl = JSON.parse(localStorage.getItem('userinfo')).imgUrl
let userid = JSON.parse(localStorage.getItem('userinfo')).id
let contentHeight = ref(document.documentElement.clientHeight - 240)
onMounted(() => {
  initSocket()
  nextTick(() => {
    // 滚动到messagesContainer的底部
    scrollToBottom();
  });
  getMessageListApi().then(res => {
    try {
      if(res.code === 0) {
        messageList.push(...res.data)
      } else {
        Message.error(res);
      }
    } catch (error) {
      console.log(error);
    }
  })
})
onUpdated(() => {
  nextTick(() => {
    // 滚动到messagesContainer的底部
    setTimeout(scrollToBottom, 20)
  });
})
const initSocket = () => {
  console.log(socket);
  // 接收到消息的回调
  socket.onmessage = messageHandler
  // 连接成功后的回调
  socket.onopen = openHandler
  // 连接发生错误的回调
  socket.onerror = errorHandler
  // 关闭的回调
  socket.onclose = closeHandler
}
const messageHandler = (e) => {
    let msg = JSON.parse(e.data)
    console.log(msg);
    messageList.push(msg)
  }
const openHandler = () => {
  console.log('open');
}
const errorHandler = () => {
  console.log('发生了错误,重连中...');
}
const closeHandler = function(event) {
  console.log('WebSocket connection closed with code: ' + event.code + ' and reason: ' + event.reason);
  setTimeout(initSocket, 1500)
}
const sendMessage = () => {
  if (!message.value) {
    return Message.warning('输入的内容不能为空')
  }
  let obj = {
    userid,
    username,
    imgUrl,
    message: message.value
  }
  socket.send(JSON.stringify(obj))
  message.value = ''
  nextTick(() => {
    // 滚动到messagesContainer的底部
    setTimeout(scrollToBottom, 20)
  });
}

const clear = () => {
  console.log('清空');
}
const scrollToBottom = () => {
  messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight + 999;
}
</script>
<style lang="less" scoped>
.right {
  // margin-left: 125px;
  float: right;
}
.title_right {
  text-align: right;
}
.float_left {
  float: left;
  margin-right: 5px;
}
  .float_right {
  float: right;
  margin-left: 5px;
}
</style>

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值