nestjs+vue+ts打造一个酷炫的星空聊天室


简介

空闲时间想做一个谈天室复盘一下这些年学习到的技能,所以在2020年6月24号就开端了Genal谈天室的开发之旅。
项目选用全typescript开发,这是为了今后的功用迭代打基础。当然,我自身也是很喜欢typescript的。

项目界面

nestjs+vue+ts打造一个酷炫的星空聊天室

功用介绍

  • 更改用户名/头像上传
  • 群聊/私聊
  • 创立群/参与群聊/含糊查找群
  • 增加老友/含糊查找老友
  • 表情包
  • 消息分页

技能概览

  • T2 v Z & O 7 q xypescript:JavaScript 的一S _ L个超集,它最大的优势是供给了类型体系和提高了代码的可读性和可保护性。
  • Vue2.6.x:前端渐进式结构。
  • Socket/io:完结实时通讯,websocket第三方w j 5库。
  • VueQ W C } Gx:专为 Vue.js 运用程序开发的状况办 r b B Y B z理形式。
  • NestjsF # $ B .:是一个用于构建高效、可扩展的 Node.js 服务端_ y w h r A A运用结构,依据 TypeScript 编写而且结O * E合了 OOP1、FP2、FRP3 的相关 y H理念。
  • Typeorm: 支撑最新的 JavaScript 特性并供给额外的特性以协助你开发任何运用数据库的运用程序。
  • ES6t g ) 6+:选用ES6+语法,箭头函数、async/await等等语法很好用。
  • SASS(SCSS):用SCSS做CSS预处理言语,能够运用最高效的方法,以少数的代码创立复杂的规划。

数据库表结构规划

数据库运用了六张表,分别是

  • user 用户表
  • group 群表
  • usergroup 用户群中心表
  • gro# [ H – | Fup_message 群消息表
  • userfriend 用户老友中心表
  • friend_message 私聊消息表

其间中心表用于P 1 Y ` n }建立关于群/老友与用户之间Y 3 K y的联络。下面是我画的思想导图,信任我们看完就能8 V ? _ i 6理解其间的奇妙啦。

nestjs+vue+ts打造一个酷炫的星空聊天室

WebSocket的建立逻辑

用户房间的建立

每个用户进入谈天S w Z d s . ) N D室都会V 3 L主动参与public谈天组,而且每个用户都会进入由用户id为名字的 WebSocket 房间,那么做是为了独自播送给某个用户然后进行一些操作。假如不了解房间的概念,能够以为只B M } f c ;有房间内的人才华接收到房间的播送,更多信息请2 f s ;移步socket.io官网。

群聊房间的建立

以groupId作为WebSock@ x 7 p K J yet房间的名字,每次有新用户参与群都会在群房间内播送用户进群工作,然后其他用户会存储新用户的信息。当新用户发消息_ J X G Z的时分,能够通过消息的userId找到对运用户的详细信息,这样能保证消息发送的速度.

私聊房间的建立

每当主张一个增加老友的恳求,就会把用户userId和老友的userId拼接成一个字符串作为WebSock( H F r net的% ~ ` z房间名,然后建立私聊房间。

后端架构

后端运用了nestjs这个近几年开展迅猛的node.js结构,不仅仅是因为其运用typescript进行开发,nestjs的依靠注入以及模块化的思想,使得代码结构清晰,便于保护。一起nestjs的@nestjs/websockets包也现已封装好了关于WebSocket工作的处理。x – 9 z @下面是一些后端的逻辑代码。

  1. 运用nestjs建立WebSocket联接
// chat.gateway.ts
@WebSocketGateway(l ` @)
export class Chay _ btGateway {
// socket衔V V o f } 7接钩子
async handleConnection(client: Socket): Promise<string> {
let userRoom = clic ) = 9 ^ s 4 cent.handshake.query.userId;
// 联接默许参与public房间
client.join('public');
// 用户独有消息房间 依据userIdh = 2 S R %
if(userRoom) {
client.join(p $ ` ^ 7userRoom);
}
return '联接成功'
}
}
  1. 封装全局的中心[ Q q X Y N G Q件,方便在开发过程中调试。
// middleware.js
export function logger(req,c 4 , res, next) {
const { method, path } = req;
console.log(`${O Z !method= L = L % 5 - b } ${path}`);
neJ : 2 9 S R  Q txt();
};
// main.js 
运用全局中心件
app.use(logger)
  1. nestjs的静态资源配备
// main.js
配备静态资源
apM q [ B C @ % Hp.useStaticAssets(join(__dirnaO H 4 D  _ & * dme, '../public/', 'static'), {
prefix: '/static/',
});
  1. nestjs0 , 6 l # z自定义失常过滤器
// http-exception} { D T  o Q u.filter.ts
@CaD ` 7  - #tch(HttpExcepto ` } ) +ion)
exporW I k q y v ; =t class HttpExceptionFilter implements ExceptionFilter<HttpExcepth 7 F Vion> {
catch(exception:! 7 v $ { HttpException, host: Ap & v * Y &rgumentsHost) {
const ctx = host.switchToHttp();k & o D
const response = ctx.getResponse();
const request = ctx.getRequest();
con/ : lst statA v y % + ` yus = exception.getStatus();
const exceptionRes: any = exception.getResponse();
const {
error,
message,
} = exceptionRes;
// 以下格s 8 : j = ! N K )局将在产生过错是回来给前端
response.status(status).= c wjson({
status,
timestamp: new Date().toISOString(),
path@ , y # v: request.url,
error,
message,
});
}
}

前端架M q H G ^ V

页面初始化

初始化会调起WebSocket衔. e * P接函数,然后拿到用户一切的群信息GroupArr和一切的老友信息FriendArr,再通过建立WebSockeW d } , S ] rt房间的规则参与到对应的房间,然后运用vuex派发最新的数据。

数据处理

群的数据类型

// 群组
interface Group {
groupId: string;
userId: string; // 群主id
groupName: string;
notice: string;
messages: GroupMessage[];
createTime: nJ r B g g 9 } g Mumber;
}

老友的数据类型

/_ ) % $ c (/ 老友
interface Friend {
userId: string;
username: string;
avatar: string;
role?: string;
tag?: string;
messages: FriendMessage[] F 5;
createTime: number;
}

我曾经用方针数组 [ Friends1f m z H , friend2 ] 这样的结@ } 6 0 @ e构去办理一切的群和一切的老友数据,但是稍微动一下脑筋就知道,这样的结构非常不利于查询/更新。每次 ! q Z j x用户的老友名字变了或许头像变了就得遍历查找一遍数组才华更新相应信息。
后来我用方针的结构,优化了谈天V x Q & |室的代码。/ T ^ k 2 ) S我运用一个方针gather来办e i L : w理群组/老友的信息, gather的键为groupId/userId, 值为对应的群/老友的信息,结构如下

gather = {
'userId': {
userId: 'userId'
username: L L Y ` & U - T 9'xxx'
messages: [];
...
}
}

每个群和用户都有绝无仅有的id,所以无需担心重复。运用这样的结构后,更新数据便十] W ? e m 3 z T $分的轻松,只需求拿到需求更新的id,然后直接掩盖gathy d m = K =er, i S S x t g $ @.id对应的值就行了

vuex

谈天室涉及到数L m K j h f # 6据的即时更新3 v 7 } C w L和各个vue组件的数据同步,处理这样的业务场景是vuex的强项。 我/ P , |把建立WebSocket联接的函数写在了vuex的action中,在用户登录成功后调起联接函数,下面是精简后的代码。

// actions.ts
cc E #onst a^ G V 1 A Lctions: ActionTree<ChatState, RootState> = {
// 初始化WebSocket
async c : E [ g WonnectSocket({commit, state, dispat P U tch, rootState}, callback) {
// WebSocket联接建立
socket.on('connect', async () => {
// 订阅群消息时间
st n C p H yocket.on('group8 + G H $ k = 6Message'k 0 Y U P ), (res: any) => {
console@ . } K R ? S ^ C.log('on groupMessagU W (e', res)
ifJ x | G } 5 - 7 i (!res.code) {
// 对群消息进行处理
commit(ADD_GROUP_MESSA` $ # f wGE, res.data)
}
})
}j T O R
}

不得不说vuex-class这个库帮A & B ! I k 9 b J了我很大的忙,它是vuex结合typx ; ]escript开发的很好的粘合剂。
运用了vuex-class,那么在vue组件中调用vuex的方法只需求这么写:

// GenalCN u f - ] 7hat.vue
impov j  z C ^ *rt { nam) U * X n 4 & Iespace } from 'vuex-class) S B f ) 1'
const appModule = namespace('app')
export default class GenalChat extends Vue {
@ap, 1 E , QpModule.Getter('user') user: User;
@appModule.Action('loginp c , m') login: Function;
}

总结

  现在谈天室现已完结日常谈天的基础功用,因为谈天室的数据结构K ~ C基本都截然不同,因此现在的谈天室架构是非常利于在此基础上进行扩展和新增功用的。一起,我往后也会陆续开发很多酷炫的功用,喜欢的朋友给个sI T Z Ptar鼓舞一下我吧!

项目地址

github: genalT ] A w & R-chat

发表评论

提供最优质的资源集合

立即查看 了解详情