前言
上一篇我们已经讲了如何使用websocket结合ndde.js实现双向通信。
下面我们来讲下,通过websocket和nodejs-websocket,构建一个小型聊天室。

先来看下实现效果:
在这里插入图片描述
在这里插入图片描述

先讲下大概思路:
客户端需要传给服务器当前登陆者的昵称、uid和发送的内容(如果有的话),服务端收到数据后,把信息同步广播给当前聊天室。
在这里插入图片描述
整体大致思路如下:
1、刚进入页面,通过 localStorage.getItem(“WEB_IM_USER”) 获取缓存里是否存储了用户信息;
2、没有用户信息,弹窗填写用户昵称;有用户信息,直接连接;
3、通过传type类型为1还是2,区分是刚进入聊天室还是发送消息,通过websocket.send()方法发送客户端消息;
4、服务端通过on(“text”, function(){})获取客户端发送的内容,并通过server.connections方法广播到客户端;
5、客户端收到信息,渲染到视图上。

上代码:

服务端:

const ws = require('nodejs-websocket')
const moment = require('moment')

function broadcast(obj) {
    server.connections.forEach(function (conn) { // 注意:这里是server,不是ws
        conn.sendText(JSON.stringify(obj)) // 注意:这里得转成字符串发送过去,不然会报错。
    })
}

const server = ws.createServer(function (conn) {
    conn.on('text', function (data) {
        const obj = JSON.parse(data)
        switch (obj.type) {
            case 1:
                broadcast({
                    type: 1,
                    nickname: obj.nickname,
                    uid: obj.uid,
                    msg: `${obj.nickname}进入了聊天室`,
                    date: moment().format('YYYY-MM-DD HH:mm:ss')
                })
                break;
            case 2:
                broadcast({
                    type: 2,
                    nickname: obj.nickname,
                    uid: obj.uid,
                    msg: obj.msg,
                    date: moment().format('YYYY-MM-DD HH:mm:ss')
                })
                break;
        }
    })

    conn.on('close', function (e) {
        console.log(e, '服务端连接关闭')
    })

    conn.on('error', function (e) {
        console.log(e, '服务端异常')
    })

}).listen(8888)
console.log('服务端已开启')

客户端:

视图层:

<template>
  <div class="home">
    <el-dialog
    :visible="showInfoDialog"
    >
      <el-input type="text" v-model="nickname" placeholder="请输入你的昵称" ></el-input>
      <span slot="footer" class="dialog-footer">
        <el-button @click="showInfoDialog = false">取 消</el-button>
        <el-button type="primary" @click="sure">确 定</el-button>
      </span>
    </el-dialog>
    <div class="right">
      <div class="body im-record" id="im-record">
        <div class="ul">
          <!-- user为靠右展示样式,如果uid一致说明是本人 -->
          <div class="li" :class="{user: item.uid == uid}" v-for="(item, index) in messageList" :key="index">
            <template v-if="item.type===1">
              <p class="join-tips">{{item.msg}}</p>
            </template>
            <template v-else>
              <p class="message-date">
                <span class="m-nickname">{{item.nickname}}</span> {{item.date}}</p>
              <p class="message-box">{{item.msg}}</p>
            </template>
          </div>
        </div>
      </div>
      <div class="im-footer">
        <el-input placeholder="请输入你想说的内容..." v-model="msg" class="im-footer_inp"/>
        <el-button class="im-footer_btn" type="primary" @click="send">发送</el-button>
      </div>
    </div>
  </div>
</template>

逻辑层:

import moment from 'moment' // 需要下载moment。npm install moment --save
export default {
  data () {
    return {
      ws: '',
      showInfoDialog: false,
      nickname: '',
      uid: this.uid,
      messageList: [],
      msg: '' // 输入的消息内容
    }
  },
  mounted () {
    this.init()
  },
  methods: {

    // 初始化
    init () {
      if (window.WebSocket) {
        let user = {}
        if (localStorage.getItem('WEB_IM_USER')) user = JSON.parse(localStorage.getItem('WEB_IM_USER'))
        this.nickname = user.nickname || ''
        this.uid = user.uid || ''

        // 没有当前人信息就弹窗去填写
        if (!this.uid) {
          this.showInfoDialog = true
        } else {
          this.contactSocket()
        }

        // 监听回车事件
        const vm = this
        document.onkeydown = function (event) {
          const e = event || window.event;
          if (e && e.keyCode == 13) {
            console.log('触发enter')
            vm.send()
          }
        }
      } else {
        console.log('当前浏览器不支持WebSocket!')
      }
    },

    // 模态框确认事件
    sure () {
      this.showInfoDialog = false
      this.contactSocket()
    },

    // enter触发 或者 点击“发送”触发
    send () {
      if (!this.msg) return;
      this.sendMessage(2, this.msg)
    },

    // 发送信息给客户端
    sendMessage (type, msg) {
      const data = {
        uid: this.uid,
        type,
        nickname: this.nickname,
        msg
      }
      this.ws.send(JSON.stringify(data))
      this.msg = ''
    },

    // 连接websocket
    contactSocket () {
      const that = this
        this.ws = new WebSocket('ws://192.168.1.124:8888')
        const ws = this.ws
        ws.onopen = function () {
          console.log('连接服务器成功')

          // 没有当前人信息的话,需要缓存下
          if (!that.uid) {
            that.uid = 'web_im_' + moment().valueOf();
            localStorage.setItem('WEB_IM_USER', JSON.stringify({
              uid: that.uid,
              nickname: that.nickname
            }))
          }
          that.sendMessage(1)
        }
        ws.onmessage = function (e) {
          that.messageList.push(JSON.parse(e.data))
        }
        ws.onclose = function () {
          console.log('连接已关闭')
        }
      }
    }
  }

样式层:

<style scoped lang="less">
  .right {
      position: relative;
      flex: 1;
      height: 600px;
      margin: 0 auto;
      .im-title {
        height: 30px;
        padding-left: 20px;
        border-bottom: 1px solid #ccc;
        line-height: 30px;
        font-size: 16px;
      }
      .im-footer {
        position: absolute;
        bottom: 0;
        left: 0;
        display: flex;
        width: 100%;
        .im-footer_inp {
          width: 80%;
        }
        .im-footer_btn {
          width: 20%;
        }
      }
      
      .im-record {
        width: 100%;
        height: 540px;
        overflow-y: auto;
        .join-tips {
          position: relative!important;
          display: block;
          width: 100%;
          left: 0!important;
          transform: none!important;
          color: #cccccc;
          font-size: 15px;
          text-align: center;
        }
        .li {
          position: relative;
          margin-bottom: 15px;
          text-align: left;
          color: #46b0ff;
          &:after {
            content: '';
            display: block;
            clear: both;
          }
          .message-date {
            font-size: 16px;
            color: #b9b8b8;
          }
          .m-nickname {
            color: #46b0ff;
          }
          &.user {
            text-align: right;
          }
        }
        .message-box {
          line-height: 30px;
          font-size: 20px;
        }
      }
    }
</style>

参考链接:Nodejs + WebSocket + Vue 实现多人聊天室WebIM功能

Logo

前往低代码交流专区

更多推荐