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 的結構圖:
在本篇博文中,這一小節只是介紹 Socket.io 中私密 Room 和公共 Room 的區別。
開始
沒有 Socket.io 基礎的同學請看入門 Socket.io。下文只介紹實現私聊功能的基本步驟,不展示細節,比如 UI 的設計。
伺服器
下面是伺服器轉交私密消息的實現步驟和程式碼:
- 伺服器監聽連接事件,當客戶端 A 連接之後,伺服器就監聽客戶端 A 的私密資訊事件。
- 當客戶端 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(組合式)程式碼,忽略了大量細節,只保留了核心程式碼:
- 導入 socket-client 模組,連接伺服器。
- 客戶端中輸入好消息之後,點擊按鈕觸發事件,客戶端發送
emit-private
給伺服器。 - 客戶端監聽伺服器轉交的私密消息。
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 標識符,它是唯一的,且由伺服器分配的。:
下圖是兩個客戶端進行私聊,另一個客戶端不能接收消息的演示截圖:
左邊是客戶端 A,右上是客戶端 B,右下是客戶端 C。客戶端 A 給客戶端 B 互相發送私密消息,客戶端 C 不能接收。
最後
想要查看完整的實現步驟,如果喜歡的話,請給我的倉庫點一個 Start 吧,再給博文也點個贊!!!
- 前端項目:gadget-chatroom
- 後端項目:gadget-chatroom-serve
目前還在繼續實現中,目的是做一個可以發送圖片和表情的,私聊、群聊功能的聊天室。