由于心血来潮想要做个聊天室项目 ,但是仔细找了一下相关教程,却发现这么多的WebSocket教程里面,很多都没有介绍详细,代码都有所残缺,所以这次带来一个比较完整得使用WebSocket的项目。
目录
一、效果展示
二、准备工作
一、前端框架,Vue + elementUI组件 +JsCookie
二、后端 SpringBoot + websocket包
三、前端代码
四、后端代码
一、效果展示
1.用户交流
二、准备工作
一、前端框架,Vue + elementUI组件 +JsCookie
新建一个vue项目
引入以下组件与依赖
npm i element-ui -S
npm install js-cookie
二、后端 SpringBoot + websocket包
创建SpringBoot项目后
在pom.xml文件中引入以下依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>
注意项目前端为8081端口,后端为8080端口,所以先运行后端再运行前端
三、前端代码
在App.vue中即可引入以下代码:
html:
<template><div id="Layout"><el-container><el-aside width="200px">Aside</el-aside><el-container><el-header style="background-color: rgb(245, 245, 245); border-bottom: 1px solid grey;"><h3>聊天广场</h3></el-header><el-main style="background-color: rgb(244, 245, 247);min-height: 700px; max-height: 700px; "><div id="chatContent" style="padding-left: 10px; line-height: normal; "><!-- 循环输出对话内容 --><el-scrollbar v-for="(message, index) in messages" :key="index"><div ref="scrollbar" v-if="message.sender !== senderName" class="chat-message" id="ChatContentCard" style=" background-color: white;margin-top: 20px; box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04) "><el-avatar :size="40"src="https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png"></el-avatar><div class="message-content" style="width: 100%;"><div style="text-align: left; text-indent: 1em;"> {{ message.sender }}</div><div id="chatContentText">{{ message.text }}</div></div></div><div ref="scrollbar" v-if="message.sender === senderName" id="myChatContentCard"><el-avatar :size="40"src="https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg"></el-avatar><div class="message-content" style="width: 100%;"><div style="text-align: left; text-indent: 1em;"> {{ message.sender }}</div><div id="chatContentText">{{ message.text }}</div></div></div></el-scrollbar></div></el-main><!-- 底层交互框 --><el-footer style="height: 190px; background-color: rgb(244, 245, 247); "><div id="Gadget" style="background-color: rgb(244, 245, 247); height: 35px; margin-bottom: 10px;"><el-upload class="upload-demo" ref="upload" action="https://jsonplaceholder.typicode.com/posts/":on-preview="handlePreview" :on-remove="handleRemove" :file-list="fileList" :auto-upload="false" style="float: left;"><el-button slot="trigger" size="small" type="primary"><i class="el-icon-picture-outline-round"></i></el-button><el-button style="margin-left: 10px;" size="small" type="success" @click="submitUpload">上传到服务器</el-button></el-upload></div><el-form @submit.native.prevent="sendMessage"style="background-color: rgb(244, 245, 247); height: 80%; width: 100%; position: relative;"><el-input v-model="messageInput" rows="4" resize="none" type="textarea" placeholder="请输入内容......."@keyup.enter="sendMessage" style="height: 100%; max-height: 60px; "></el-input><div style="text-align: right; background-color: rgb(244, 245, 247); margin-top: 34px;"><el-button type="primary" @click="sendMessage">发送</el-button></div></el-form></el-footer></el-container></el-container></div>
</template>
script:
<script>
import Cookies from 'js-cookie';export default {computed: {senderName() {return Cookies.get('account') || '游客';}},name: 'App',data() {return {messages: [],messageInput: '',ws: null,fileList: []};},mounted() {this.initWebSocket();},beforeDestroy() {this.closeWebSocket();},methods: {initWebSocket() {this.ws = new WebSocket('ws://localhost:8080/chat');this.ws.onopen = () => {console.log('Connected to server.');};this.ws.onmessage = (event) => {try {let messageData;if (isJson(event.data)) {messageData = JSON.parse(event.data);} else {messageData = { text: event.data };}this.messages.push({sender: messageData.sender || 'Anonymous',text: messageData.text,});// 使用Vue.nextTick确保DOM更新后再执行滚动操作this.$nextTick(() => {// 确保scrollbar存在且已渲染if (this.$refs.scrollbar) {// 直接滚动到底部,不需要使用contentSize// this.$refs.scrollbar.$el.scrollTop = this.$refs.scrollbar.$el.scrollHeight;}});} catch (error) {console.error('Error parsing message:', error);}};// 辅助函数,检查字符串是否可能是JSON格式function isJson(str) {try {JSON.parse(str);} catch (e) {return false;}return true;}this.ws.onerror = (error) => {console.error('WebSocket error:', error);};this.ws.onclose = () => {console.log('Disconnected from server.');};},sendMessage() {console.log('调用sendMessage');const senderName = Cookies.get('account');if (senderName === null) {this.senderName = "游客";}if (this.messageInput.trim() !== '') {this.ws.send(JSON.stringify({ sender: senderName, text: this.messageInput }));this.messageInput = ''; // 清空输入框}},closeWebSocket() {if (this.ws && this.ws.readyState === WebSocket.OPEN) {this.ws.close();}},// 文件上传函数
submitUpload() {this.$refs.upload.submit();},handleRemove(file, fileList) {console.log(file, fileList);},handlePreview(file) {console.log(file);}},
};
</script>
css:
<style scoped>
.chat-message {display: flex;align-items: center;margin-bottom: 10px;
}.message-content {margin-left: 10px;
}#Layout {line-height: normal;
}/* 添加动画关键帧 */
@keyframes slideInFromLeft {0% {transform: translateX(-100%);opacity: 0;}100% {transform: translateX(0);opacity: 1;}
}#ChatContentCard {min-height: 80px;width: 50%;/* 应用动画 */animation: slideInFromLeft 0.3s ease-in-out forwards;border-radius: 30px
}#chatContentText {width: 99%;overflow-wrap: break-word;}.el-header,
.el-footer {background-color: #B3C0D1;color: #333;text-align: center;line-height: 60px;
}.el-aside {background-color: #D3DCE6;color: #333;text-align: center;line-height: 200px;
}.el-main {background-color: #E9EEF3;color: #333;text-align: center;line-height: 160px;
}body>.el-container {margin-bottom: 40px;
}.el-container:nth-child(5) .el-aside,
.el-container:nth-child(6) .el-aside {line-height: 260px;
}.el-container:nth-child(7) .el-aside {line-height: 320px;
}#myChatContentCard{display: flex;align-items: center;margin-bottom: 10px;margin-left: 50%;min-height: 80px;width: 50%;/* 应用动画 */animation: slideInFromRight 0.3s ease-in-out forwards;border-radius: 30px;background-color: rgb(149, 236, 105);margin-top: 20px; box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04) }@keyframes slideInFromRight {0% {transform: translateX(100%);opacity: 0;}100% {transform: translateX(0);opacity: 1;}
}</style>
注意: 这个项目中如果script需要进行修改,由于我这里完成了一个登陆系统,所以采用了对Cookie的使用,而如果只是体验的话,只需要把Cookie去掉将其改为游客+随机字符串去替代即可。
前端启动
npm run serve
四、后端代码
WebSocket配置:
1.WebSocketConfig.java
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {registry.addHandler(new ChatWebSocketHandler(), "/chat").setAllowedOrigins("*");}
}
2.ChatWebSocketHandler.java
@Slf4j
public class ChatWebSocketHandler extends TextWebSocketHandler {private static final Set<WebSocketSession> sessions = Collections.synchronizedSet(new HashSet<>());@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {sessions.add(session);broadcast("欢迎新的小伙伴加入");}@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {broadcast(message.getPayload());}private void broadcast(String message) {log.info("服务器广播数据:"+message);sessions.forEach(session -> {try {session.sendMessage(new TextMessage(message));} catch (Exception e) {e.printStackTrace();}});}@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {sessions.remove(session);}
}
后端启动: