iT邦幫忙

2021 iThome 鐵人賽

DAY 27
1
Modern Web

摸索 WebSocket,遠望 WebRTC系列 第 27

Day26:優化修正

前面在寫傳送資料這一塊時,有些環節沒有思考妥當,導致問題叢生,所以這邊先進行部分重新調整。

登入和發送訊息本質都是事件,差別在於,當登入事件觸發時,必須告訴 server,現在登入的人是誰?他要登入哪個頻道?而訊息發送則是這兩個參數之上,要額外增加,現在送出的訊息是什麼?所以梳理如下:

  • logingChat()

    • event(觸發什麼事件)
    • username(登入者是誰?)
    • channelId(他輸入的哪個頻道?是預設的 Public,還是自定義)
  • sendMessage()

    • event(觸發什麼事件)
    • username(發送訊息的人是誰?)
    • channelId(目前所處的頻道)
    • message(訊息內容)

所以根據這個結構,調整下面的 HTML, client.js, server.js。

HTML

重新調整了 HTML 結構,當進入聊天室頁面時,左側放置聊天訊息,右側則顯示進入頻道的人和離開頻道者。同時替文字簡單加一些顏色,方便區分。

<!DOCTYPE html>
<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/vue@2.6.14"></script>
    <link rel="stylesheet" href="./styles.css" />
  </head>
  <body>
    <div id="app">
      <div v-if="isShow" class="login-wrapper">
        <p>
          暱稱 :
          <input v-model="username" type="text" />
        </p>
        <p>
          頻道 :
          <input v-model="channelId" type="text" />
        </p>
        <button @click="logingChat()">進入聊天室</button>
      </div>
      <div v-else class="chat-wrapper">
        <h2>目前線上人數 : {{ onlineNum }}</h2>
        <div class="channel">
          <div>
            <input v-model="message" class="input-message" type="text" />
            <button type="button" class="send-btn" @click="sendMessage()">
              送出訊息
            </button>
            <ul class="message-wrapper">
              <li v-for="(item, index) in messageList" :key="index + item">
                <span class="user-name">{{ item.username }}</span> : {{
                item.message }}
              </li>
            </ul>
          </div>
          <div class="user-list">
            <div v-for="(item, index) in userList" :key="index + item">
              <div v-if="item.event === 'login'">
                歡迎 :
                <span class="user-name">{{ item.username }}</span> 進入頻道
              </div>
              <div v-else>
                <span class="user-name">{{ item.username }}</span> 已經離開頻道
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

    <script src="./client.js"></script>
  </body>
</html>
// styles.css

.channel {
  display: flex;
}

.user-name {
  color: #c37b89;
}

.user-list {
  margin-left: 30px;
  color: #bccc9a;
  font-size: 12px;
}

Client

data 的預設值中,區分出訊息列表和登入者列表

data: {
    messageList: [],
    userList: [],
},

methods: {
    onMessage(event) {
      let obj = JSON.parse(event.data);
      this.onlineNum = obj.num;
      if (obj.event === 'message') {
        this.messageList.push(obj);
      } else {
        this.userList.push(obj);
      }
    },
}

另外送給 server 的參數,依照最前面所討論的結構去設定

logingChat() {
  if (this.username.trim() === '') {
    alert('請輸入姓名');
    return;
  }
  this.isShow = false;
  this.ws.send(
    JSON.stringify({
      event: 'login',
      username: this.username,
      channelId: this.channelId,
    })
  );
},
sendMessage() {
  this.ws.send(
    JSON.stringify({
      event: 'message',
      username: this.username,
      channelId: this.channelId,
      message: this.message,
    })
  );
  this.message = '';
},

Server

當 server 拿到參數資料後,除了回傳訊息外,如果監聽到使用者離線了,也要送出離線者的資料

wss.on('connection', function connection(ws) {
  ws.on('message', function (data) {
    const bufferData = Buffer.from(data).toString();
    let formData = JSON.parse(bufferData);
    ws.channelId = formData.channelId;
    ws.username = formData.username;

    wss.clients.forEach((client) => {
      if (
        client.readyState === WebSocket.OPEN &&
        client.channelId === ws.channelId
      ) {
        formData.num = wss.clients.size;
        client.send(JSON.stringify(formData));
      }
    });
  });

  ws.on('close', (data) => {
    let formData = {};
    wss.clients.forEach((client) => {
      if (client.readyState === WebSocket.OPEN && ws.username) {
        formData.event = 'out';
        formData.num = wss.clients.size;
        formData.username = ws.username;
        client.send(JSON.stringify(formData));
      }
    });
  });
});


上一篇
Day25:複數聊天室
下一篇
Day27:歪樓+卡文(全英文筆記 - I)
系列文
摸索 WebSocket,遠望 WebRTC30

尚未有邦友留言

立即登入留言