天天看點

websocket+vue+node線上群聊

websocket+vue+node線上群聊

最近剛寫了一個線上群聊的功能。

一個是vue頁面;

一個是背景服務js;需要先下載下傳ws

<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
  <style>
  .after {
  display: flex;
}
.after .left .content {
  width: 500px;
  height: 300px;
  border: solid 1px red;
  overflow: scroll;
  overflow-x: hidden;
  padding: 0 20px;
}
.after .left .content .notice {
  text-align: center;
}
.after .left .content .myMessage {
  text-align: right;
}
.after .left .content .message {
  border: solid 2px black;
  display: inline-block;
}
.after .right {
  width: 200px;
  height: inherit;
  border: solid 1px blue;
}
.after .right .isMe {
  color: red;
}

</style>
</head>

<body>
  <div id="app">
    <!-- 先輸入昵稱 -->
    <div class="before" v-if="!ifEnter">
      <input v-model="userName" :value="userName" search enter-button="确定"
        placeholder="請輸入您的昵稱" />
        <button @click="settingName">确定</button>
    </div>
    <!-- 開始群聊 -->
    <div class="after" v-else>
      <div class="left">
        <!-- 聊天内容 -->
        <div class="content" id="content">
          <div v-for="(item,index) in messageList" :key="index">
            <!-- 加入群聊 -->
            <div class="notice" v-if="item.type=='addUser'">
              <h2>{{item.user}}加入群聊</h2>
            </div>
            <!-- 退出群聊 -->
            <div class="notice" v-if="item.type=='quit'">
              <div>{{item.user}}退出群聊</div>
            </div>
            <!-- 收到消息 -->
            <div :class="{'myMessage':item.userId==userId}" v-if="item.type=='message'">
              <div>{{item.user}}:{{item.time}}</div>
              <h3 class="message">{{item.content}}</h3>
            </div>
          </div>
        </div>
        <!-- 編輯欄 -->
        <div>
          <input v-model="text" style="width: 200px;" />
          <button type="info" @click="send()">發送</button>
          <button type="error" @click="quit()">退出</button>
        </div>
      </div>
      <!-- 使用者資訊 -->
      <div class="right">
        <h1>《使用者清單》</h1>
        <div v-for="(item,index) in users" :key="index" :class="{'isMe':item.userId==userId}">{{item.userName}}(
          {{item.userId}} )</div>
      </div>
    </div>
  </div>
</body>
<script>
  new Vue({
    el:'#app',
    data:{
        messageList: [], //所有消息清單
        users: [], //使用者清單
        ifEnter: false, //是否聊天狀态
        userName: '遊客',
        userId: '',
        text: '大家好 丫~我是婷婷兒', //内容
        ws: null, //wbs
        message: { //發送的消息格式
          type: '', //發送類型
          user: '',
          userId: '',
          toUser: '', //消息接收方名
          toUser: '', //消息接收方id
          content: '', //發送内容
          time: '' //發送時間
        }
    },
    methods: {
      // 退出聊天室
      quit() {
        this.message.type = 'quit';
        this.message.content = this.text;
        this.ws.onopen();
        this.ws.close();
        this.ifEnter = false;

      },
      // 設定昵稱
      settingName() {
        this.ifEnter = true;
        this.userId = new Date().getTime();
        this.message.type = 'addUser';
        this.message.user = this.userName;
        this.message.userId = this.userId;
        this.message.time = new Date().toLocaleString()
        this.initWbs();
      },
      // wbs初始化;
      initWbs() {
        this.ws = new WebSocket(`ws://localhost:8080/chat?userId=${this.message.userId}&userName=${this.userName}`);
        // 設定屬性
        // 連接配接時
        this.ws.onopen = (e) => {
          var sendMessage = JSON.stringify(this.message);
          this.ws.send(sendMessage)
        };
        // 收到資訊時
        this.ws.onmessage = (e) => {
          var recieve = JSON.parse(e.data);
          switch (recieve.type) {
            // 首次連接配接會發送所有使用者資訊
            case 'allUser':
              this.users = recieve.userList;
              break;
            // 添加使用者
            case 'addUser':
              break;
            // 退出消息
            case 'quit':
              // 獲得退出使用者資訊,從清單中去掉;
              this.users = this.users.filter((element, index) => {
                if (recieve.userId != element.userId) {
                  return element;
                }
              })
              break;
            // 普通消息
          }
          if (recieve.type != 'allUser') {
            this.messageList.push(recieve);
            // ue中資料和dom渲染由于是異步的,是以,要讓dom結構随資料改變這樣的操作都應該放進this.$nextTick()的回調函數中。
            this.$nextTick(() => {
              var ele = document.getElementById('content');
              ele.scrollTop = ele.scrollHeight - ele.clientHeight;
            })
          }
        };
        // 退出時
        this.ws.onclose = () => {
          this.users = [];
          console.log(this.users)
          this.messageList = [];
          console.log("你已經退出聊天室了!!");
        };
      },
      send() {
        this.message.type = 'message';
        this.message.content = this.text;
        this.ws.onopen();
      }
    }
  })

</script>

</html>
           
var WebSocket = require('ws');
var wsList = [];//連接配接使用者清單
var usersInfo = {
  type: 'allUser',
  userList: []
};
const wss = new WebSocket.Server({ port: 8080 });
function wbs() {
  wss.on('connection', function connection(ws, req) {
    // 第一次連接配接消息時
    connectNow(ws, req)
    // 接受消息時
    ws.on('message', function incoming(data) {
      // 根據發送的data分類
      message(ws, data);
    });
  });
}
// 發現使用者退出群以後,從清單上去掉,再群發使用者資訊;
function message(ws, data) {
  var getDate = JSON.parse(data);
  switch (getDate.type) {
    // 使用者退出
    case 'quit':
      var nowUserList = usersInfo.userList.filter(element => {
        if (element.userId != getDate.userId) {
          return element;
        }
      })
      usersInfo.userList = nowUserList;//更新使用者清單
      // 群發退出的使用者資訊
      wsList.forEach((client) => {
        if (client.readyState === WebSocket.OPEN) {
          client.send(data);
        }
      });
      break;
    case 'addUser':
      // 增加使用者
      wsList.forEach((client) => {
        if (client.readyState === WebSocket.OPEN) {
          client.send(data);
        }
      });
      break;
    case 'message':
      // 普通消息群發
      wsList.forEach((client) => {
        if (client.readyState === WebSocket.OPEN) {
          client.send(data);
        }
      });
      break;
  }
}

// 第一次連接配接以後伺服器給前端發送消息
function connectNow(ws, req) {
  var info = getParams(req.url);
  ws.info = info;
  wsList.push(ws);
  usersInfo.userList.push(info)
  wsList.forEach((client) => {
    if (client.readyState === WebSocket.OPEN) {
      client.send(JSON.stringify(usersInfo));
    }
  });
}
// 解析參數
function getParams(url) {
  var params = url.split('?')[1].split('&');
  var paramsList = {};
  params.forEach(element => {
    var info = element.split('=');
    if (info[0]) {
      paramsList[decodeURIComponent(info[0])] = decodeURIComponent(info[1])
    }
  })
  return paramsList;
}
module.exports = wbs;