Vue3 + Socket.io + Knex + TypeScript 實現可以私聊的聊天室

前言

下文只在介紹實現的核心程式碼,沒有涉及到具體的實現細節,如果感興趣可以往下看,在文章最後貼上了倉庫地址。項目採用前後端模式,前端使用 Vite + Vue3 + TS;後端使用 Knex + Express + TS。目前項目還沒有完全實現,文章的目的是記錄階段性「勝利」和分享知識。

關於搭建 TS 項目請看搭建 Webpack + TypeScript + Babel 的項目或者JavaScript遷移

Room 的概念

私密 Room

在開始做私聊功能之前,要掌握 Socket.io 的 Room 概念,當一個客戶端連接到伺服器時就會產生一個唯一的標識符,作為客戶端的 ID。socket.id就可以拿到標識符:

server.on("connection", (socket: any) => {
  console.log(socket.id);
});

一個socket.id就是一個私密的 Room,客戶端 A 拿到客戶端 B 的私密 Room,就可以通過socket.to(room).emit('echo private', "Hello")向客戶端 B 發送私密資訊。

公共 Room

任意一個字元都可以作為公共 Room,區別於私密 Room。客戶端通過socket.join(room)加入公共 Room,伺服器就可以使用server.to(room).emit('public')發送群聊消息的事件,而客戶端只需要監聽這個事件就可以拿到群聊消息。下圖是公共 Room 的結構圖:

image

在本篇博文中,這一小節只是介紹 Socket.io 中私密 Room 和公共 Room 的區別。

開始

沒有 Socket.io 基礎的同學請看入門 Socket.io。下文只介紹實現私聊功能的基本步驟,不展示細節,比如 UI 的設計。

伺服器

下面是伺服器轉交私密消息的實現步驟和程式碼:

  1. 伺服器監聽連接事件,當客戶端 A 連接之後,伺服器就監聽客戶端 A 的私密資訊事件。
  2. 當客戶端 A 發送私密資訊事件之後,伺服器就把私密資訊轉發給客戶端 B。
server.on("connection", (socket: any) => {
  socket.on("emit-private", (e: any) => {
    socket.to(e.socket_id).emit("echo-private", e);
  });
});

客戶端

客戶端的實現步驟稍微複雜一些,客戶端發送的私密消息需要封裝到類,通過 emit 發送給伺服器,再由伺服器去轉交消息給目標客戶端。

(1)封裝消息類:

export class Message {
  public username: string;
  public text: string;
  public avatar: string;
  public popColor: string;
  public type?: string;
  public socket_id?: string;

  constructor(username: string, text: string, avatar: string, popColor: string, type?: string, socketId?: string) {
    this.username = username;
    this.text = text;
    this.avatar = avatar;
    this.popColor = popColor;
    this.type = type;
    this.socket_id = socketId;
  }
}

接收私密消息一方需要知道消息的用戶名、消息文本、頭像、Socket ID(私密 Room 標識符),其他的欄位忽略不看。

(2)發送私密消息

下面是聊天室的 setup(組合式)程式碼,忽略了大量細節,只保留了核心程式碼:

  1. 導入 socket-client 模組,連接伺服器。
  2. 客戶端中輸入好消息之後,點擊按鈕觸發事件,客戶端發送emit-private給伺服器。
  3. 客戶端監聽伺服器轉交的私密消息。
import { onMounted, ref } from "vue";
import { io } from "socket.io-client";
import { Message } from "../typescript/standard";

const socket = io("//localhost:3000");

onMounted(() => {
  socket.on("echo-private", (e) => {
    console.log(e);
  });

  methods.onSendText = (text: string) => {
    let message = new Message(username, text, avatar, popColor, "others", socket_id);
    socket.emit("emit-private", message);
  };
});

演示

目前有三個客戶端已經連接到伺服器,下圖是每一個客戶端的私密 Room 標識符,它是唯一的,且由伺服器分配的。:

image

下圖是兩個客戶端進行私聊,另一個客戶端不能接收消息的演示截圖:

image

左邊是客戶端 A,右上是客戶端 B,右下是客戶端 C。客戶端 A 給客戶端 B 互相發送私密消息,客戶端 C 不能接收。

最後

想要查看完整的實現步驟,如果喜歡的話,請給我的倉庫點一個 Start 吧,再給博文也點個贊!!!

  1. 前端項目:gadget-chatroom
  2. 後端項目:gadget-chatroom-serve

目前還在繼續實現中,目的是做一個可以發送圖片和表情的,私聊、群聊功能的聊天室。