Nodejs + WebSocket + Vue 一對一、一對多聊天室 - 第三章
前言
如果你看到這篇文章,還沒有了解前面2篇文章的同學,可以先去了解一波,這樣上手更快。
推薦文章:
這篇文章都是前面文章的加強版,功能升級。給大家提供一個循序漸進的學習過程,一步一步的來。
在第二篇文章結束時,我們就已經可以一對多的聊天了,就是多人群聊。這次,我們進行擴充套件來實現一對一、一對多功能。
WebSocket客戶端UI介面更改
有了一對一,一對多,我們就需要對直接的介面做出調整了。左邊顯示聊天人員列表,右邊是具體訊息列表。
<div class="web-im">
<div class="dis-flex">
<div class="user-list">
<div class="user" @click="triggerGroup">群1</div>
<div class="user" @click="triggerPersonal(item)" v-if="item.uid!=uid" v-for="item in users">{{item.nickname}}</div>
</div>
<div class="msg-content">
<div class="header im-title">{{title}}</div>
<div class="content im-record">
<div class="li" :class="{user: item.uid == uid}" v-for="item in currentMessage">
<template v-if="item.type===1">
<p class="join-tips">{{item.msg}}</p>
</template>
<template v-else>
<div class="img">{{item.nickname}}</div>
<p class="message-box">{{item.msg}}</p>
</template>
</div>
</div>
<div class="footer im-input">
<input type="text" v-model="msg" placeholder="請輸入內容">
<button @click="send">傳送</button>
</div>
</div>
</div>
</div>
這裡我們就寫死了一個群,叫群1,預設是所用使用者進去群聊。
WebSocket服務端
var ws = require("nodejs-websocket");
var moment = require('moment');
console.log("開始建立連線...")
let users = [];
let conns = {};
function boardcast(obj) {
// bridge用來實現一對一的主要引數
if(obj.bridge && obj.bridge.length){
obj.bridge.forEach(item=>{
conns[item].sendText(JSON.stringify(obj));
})
return;
}
server.connections.forEach((conn, index) => {
conn.sendText(JSON.stringify(obj));
})
}
var server = ws.createServer(function(conn){
conn.on("text", function (obj) {
obj = JSON.parse(obj);
// 將所有uid對應的連線conn存到一個物件裡面
conns[''+obj.uid+''] = conn;
if(obj.type===1){
let isuser = users.some(item=>{
return item.uid === obj.uid
})
if(!isuser){
users.push({
nickname: obj.nickname,
uid: obj.uid
});
}
boardcast({
type: 1,
date: moment().format('YYYY-MM-DD HH:mm:ss'),
msg: obj.nickname+'加入聊天室',
users: users,
uid: obj.uid,
nickname: obj.nickname,
// 增加引數
bridge: obj.bridge
});
} else {
boardcast({
type: 2,
date: moment().format('YYYY-MM-DD HH:mm:ss'),
msg: obj.msg,
uid: obj.uid,
nickname: obj.nickname,
// 增加引數
bridge: obj.bridge
});
}
})
conn.on("close", function (code, reason) {
console.log("關閉連線")
});
conn.on("error", function (code, reason) {
console.log("異常關閉")
});
}).listen(8001)
console.log("WebSocket建立完畢")
主體結構還是和第二章型別,不同的是:
1、每次將uid對應的conn儲存到一個物件conns上
2、根據客戶端傳入的引數bridge來判斷,是群發還是一對一傳送
3、群發還是第二章邏輯即可
server.connections.forEach((conn, index) => {
conn.sendText(JSON.stringify(obj));
})
4、一對一傳送,bridge裡面是一對一的兩個使用者uid,這樣就可以在conns物件上找到uid對應的連線conn,並用conn傳送資訊即可
if(obj.bridge && obj.bridge.length){
obj.bridge.forEach(item=>{
conns[item].sendText(JSON.stringify(obj));
})
return;
}
WebSocket客戶端
export default {
...
data(){
return {
title: '群聊',
uid: '',
nickname: '',
socket: '',
msg: '',
// 當前使用者所有訊息
messageList: [],
users: [],
bridge: []
}
},
mounted() {
...
},
computed: {
// 當前對話渲染的msg列表
currentMessage() {
let vm = this;
// 篩選只有bridge相同的對話,展示出來
// 陣列比較,先轉成字串
let data = vm.messageList.filter(item=>{
return item.bridge.sort().join(',') == vm.bridge.sort().join(',')
})
return data;
}
},
methods: {
// 切換到群聊
triggerGroup() {
this.bridge = [];
this.title = '群聊';
},
// 切到具體個人
triggerPersonal(item) {
if(this.uid === item.uid){
return;
}
// 將當前使用者uid,和需要對話的uid放入bridge
this.bridge = [this.uid, item.uid];
this.title = '和' + item.nickname + '聊天';
},
send(){
if(!this.msg){
return
}
this.sendMessage(2, this.msg)
},
sendMessage(type, msg){
this.socket.send(JSON.stringify({
uid: this.uid,
type: type,
nickname: this.nickname,
msg: msg,
// 增加bridge引數
bridge: this.bridge
}));
this.msg = '';
},
conWebSocket(){
let vm = this;
if(window.WebSocket){
vm.socket = new WebSocket('ws://localhost:8001');
let socket = vm.socket;
socket.onopen = function(e){
console.log("連線伺服器成功");
if(!vm.uid){
...
}
// 這裡將sendMessage方法if外面
vm.sendMessage(1)
}
socket.onclose = function(e){
console.log("伺服器關閉");
}
socket.onerror = function(){
console.log("連線出錯");
}
// 接收伺服器的訊息
socket.onmessage = function(e){
let message = JSON.parse(e.data);
vm.messageList.push(message);
if(message.users) {
vm.users = message.users;
}
}
}
},
login(){
...
}
}
}
上方…的程式碼區域都是和第二篇文章一樣的地方,所有就省略了。
1、預設是群發,即bridge是空陣列,向所有使用者傳送訊息
2、點選使用者列表,賦予bridge當前使用者uid,和需要對話的uid。
3、在第二篇文章中,渲染的訊息列表是messageList。現在不是,是通過計算屬性computed,只需要bridge相等的訊息,得出currentMessage當前對話的訊息列表
4、因為所有訊息都是通過後臺socket返回,也不需要考慮傳送者/接收者是誰,判斷bridge是否相等,可以用sort()方法排序並轉換成字串後進行對比。
快速預覽效果
總結
一對一和一對多的核心,就是知道是那個使用者與那個使用者對話。當前目前一對多是不用考慮,因為是寫死的,所有使用者,後面來做不同群,隨意加群聊天,就需要考慮了。也就是服務端的conn不要弄錯,不然收不到訊息也接收不到訊息。
相關文章
- Node+WebSocket+Vue 一對一、一對多聊天室訊息已讀未讀WebVue
- Java聊天室——一對一模式Java模式
- JPA(3) 表關聯關係(多對一、一對多、多對多、一對一)
- gorm 關係一對一,一對多,多對多查詢GoORM
- MyBatis表關聯 一對多 多對一 多對多MyBatis
- Spring Data JPA 之 一對一,一對多,多對多 關係對映Spring
- Mybatis一對多、多對一處理MyBatis
- mybatis一對多&&多對一處理MyBatis
- MyBatis07-(多對一、一對多)MyBatis
- Mybatis09_一對一、一對多、多對多、延遲載入MyBatis
- Mybatis【一對多、多對一、多對多】知識要點MyBatis
- mybatis入門基礎(六)----高階對映(一對一,一對多,多對多)MyBatis
- spring data jpa關聯查詢(一對一、一對多、多對多)Spring
- JPA中對映關係詳細說明(一對多,多對一,一對一、多對多)、@JoinColumn、mappedBy說明APP
- mybatis的一對多,多對一,以及多對對的配置和使用MyBatis
- EF:Fluent API 把一對多對映為一對一API
- MyBatis 使用resultMap 以及 一對一和一對多MyBatis
- Rails 一對多AI
- 多對一處理 和一對多處理的處理
- Spring Boot 入門系列(二十八) JPA 的實體對映關係,一對一,一對多,多對多關係對映!Spring Boot
- 開發一對一直播聊天室一對一表演按分鐘賺錢的軟體系統。
- jpa一對多查詢
- 模型關聯一對多模型
- #MyBatis多表查詢 #多對一、一對多的兩種實現方式 @FDDLCMyBatis
- [增刪改查] 最規範的 JPA 一對多/多對一 CRUD 示例
- 一對一直播交友帶社群匹配聊天一對多直播交友appAPP
- FastAPI(56)- 使用 Websocket 打造一個迷你聊天室ASTAPIWeb
- 基於webapi的websocket聊天室(番外一)WebAPI
- hibernate(三) 一對多對映關係
- [增刪改查] 最簡單的 JPA 一對多/多對一 CRUD 設計
- 基於websocket與nodejs-websocket的簡單聊天室WebNodeJS
- GoldenGate多對一複製Go
- 【Evil 域】SQL函式——將一對多關係轉換成一對一關係SQL函式
- 會議室無線投屏模式:一對多投屏與多對一投屏模式
- 學習WebSocket(二):使用Spring WebSocket做一個簡單聊天室WebSpring
- 基於MyEclipse的Hibernate的多對一和一對多操作簡單示例Eclipse
- MyBatis加強(1)~myBatis物件關係對映(多對一關係、一對多關係)、延遲/懶載入MyBatis物件
- Hibernate對映檔案一對多關係薦