基于Vue + Socket.io + Axios + Express实现的在线多人聊天室

hzqsns 2022-01-19 12:45:38
  • 项目概述
    • 本项目基于websocket实现了在线多人的实时聊天室,其中前端部分由Vue实现,后端部分由nodejs中的express框架实现
    • 项目实现了登录界面,用户在输入自己的昵称后即登录成功,支持多用户登录并实时共享消息
  • 项目所用技术介绍
    • Vue:Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
      • Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension (opens new window),提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。
      • 说白了,vuex就是用来管理数据的。
      • Vuex的核心就是store(仓库),其包含应用中的大部分状态。Vuex 和单纯的全局对象有以下两点不同:

        • Vuex的状态存储时响应式的
        • store中状态不能直接改变
      • 其他组成部分
        • getter: 类似于 Vue 中的计算属性,根据其他 getter 或 state 计算返回值。

        • mutation: 一组方法,是改变store中状态的执行者,只能是同步操作。

        • action: 一组方法,其中可以包含异步操作。

    • Socket.ioSocket.IO 是一个面向实时 web 应用的 JavaScript 库。它使得服务器和客户端之间实时双向的通信成为可能。他有两个部分:在浏览器中运行的客户端库,和一个面向Node.js的服务端库。两者有着几乎一样的API。像Node.js一样,它也是事件驱动的.

      Socket.IO 主要使用WebSocket协议。但是如果需要的话,Socket.io可以回退到几种其它方法,例如Adobe Flash Sockets,JSONP拉取,或是传统的AJAX拉取,并且在同时提供完全相同的接口。尽管它可以被用作WebSocket的包装库,它还是提供了许多其它功能,比如广播至多个套接字,存储与不同客户有关的数据,和异步IO操作

    • Axios:Axios是基于Promise对象实现的可以用于浏览器和node.js的网络请求库。在服务端它使用原生 node.js http 模块, 而在客户端 (浏览器) 则使用 XMLHttpRequest对象。简单的讲就是可以发送get、post请求。

    • ExpressExpress 是最流行的 Node 框架,是许多其它流行 Node 框架 的底层库。它提供了以下机制:

      • 为不同 URL 路径中使用不同 HTTP 动词的请求(路由)编写处理程序。
      • 集成了“视图”渲染引擎,以便通过将数据插入模板来生成响应。
      • 虽然 Express 本身是极简风格的,但是开发人员通过创建各类兼容的中间件包解决了几乎所有的 web 开发问题。这些库可以实现 cookie、会话、用户登录、URL 参数、POST 数据、安全头等功能。

      • 设置常见 web 应用设置,比如用于连接的端口,以及渲染响应模板的位置。
      • 在请求处理管道的任何位置添加额外的请求处理“中间件”。
  • 项目设计
    • 需求分析
      • web端:
        • 用户注册聊天室
        • 用户登录聊天室
        • 用户接受其他用户发送的消息
        • 用户向聊天室中发送消息
      • 服务端
        • 记录用户信息
        • 记录用户历史发言记录
    • 项目UML图

       

  • 项目展示
    • 前端界面

       

      • 登录界面
      • 登录成功

         

      • 聊天界面

         

    • 项目细节
      • 代码组织结构

         

        • web端代码主要在Login.vue和Main.vue中实现
        • 服务端代码主要在server.js中实现
        • 而且我们使用Vuex进行用户信息和历史消息的维护
      • 具体代码分析
        • 用户登录模块
          • 前端登录界面的实现,这里借助了ElementUI进行实现
          • <template>
              <div>
                <el-row type="flex" justify="center">
                  <el-form
                    :model="user"
                    label-width="100px"
                    :rules="rules"
                    status-icon
                    ref="loginForm"
                  >
                    <el-form-item label="名称" prop="name">
                      <el-input v-model="user.name"></el-input>
                    </el-form-item>
                    <el-form-item>
                      <el-button type="primary" icon="el-icon-upload" @click="login">
                        登录
                      </el-button>
                    </el-form-item>
                  </el-form>
                </el-row>
              </div>
            </template>
          • 登录界面的代码逻辑

            • <script>
              
              export default {
                data() {
                  return {
                    user: {},
                    rules: {
                      name: [{ required: true, message: "名称不能为空", trigger: "blur" }]
                    }
                  };
                },
              
                mounted() {
                },
              
                methods: {
                  login() {
                    this.$refs.loginForm.validate(valid => {
                      if (valid) {//如果用户名有效(不为空)的话
                        if (this.user.name) {
                          this.$store.dispatch("login", this.user).then(() => { //存放数据到vuex里面
                           
                            this.$notify({//用户登录之后,弹窗出用户登录成功的信息
                              type: "success",
                              message: "欢迎," + this.user.name + "!",
                              duration: 2000
                            });
              
                            this.$router.replace("/chat");//页面的跳转
                          });
                        } else {//如果用户名无效(为空)的话,报错
                          this.$message({
                            showClose: true,
                            message: "名称不能为空哦",
                            type: "error"
                          });
                        }
                      } else {
                        return false;
                      }
                    });
                  }
                }
              };
              </script>
        • 聊天界面对应UI

          • <template>
              <div>
                <div>
                  <div>
                    欢迎,{{ user.name }}
                    <el-button
                      style="margin-left:20px"
                      size="mini"
                      type="plain"
                      @click="logout"
                      >退出</el-button
                    >
                  </div>
                </div>
            
                <div class="fixed-bottom" v-if="user">
                  <div style="margin-top:15px">
                    <div style="margin-top:15px" v-for="msg in messages" :key="msg">
                      <div>{{ msg }}</div>
                    </div>
                  </div>
                  <el-input style="margin-top:15px" @keyup.enter.native="sendMsg" v-model="inputMsg"></el-input>
                  <el-button style="margin-top:15px" @click="sendMsg">发送</el-button>
                </div>
              </div>
            </template>
            
            <style>
            .fixed-bottom {
              position: absolute;
              bottom: 20px;
            }
            </style>
        • 前端部分代码逻辑实现

          • <script>
            import io from "socket.io-client";
            //fixme
            export default {
              data() {
                return {
                  messages: [],
                  inputMsg: "",
                  //设置一个url和端口
                  backendUrl: "http://192.168.1.109:9999",
                };
              },
            
              created() {},
            
              mounted() {
                console.log("new io");
                let newSocket = io(this.backendUrl); //和后端建立一个socket.io链接
                this.$store.commit("connect", newSocket); //把数据存放到vuex中
            
                this.mySocket.on("msg", (data) => {
                  //前端的监听,监听后端发送的消息
                  try {
                    data = JSON.parse(data);
                    let msg = data.username + ": " + data.content;
                    this.messages.push(msg); //不能直接修改message,只能用push修改
                  } catch (error) {
                    this.$notify(error); //如果发生错误的话直接报错
                  }
                });
                this.mySocket.emit("username", this.user.name);
            
                //axios
                // this.startPolling()
              },
            
              methods: {
                logout() {
                  this.mySocket.disconnect();
                  this.$store.commit("logout");
                  this.$router.replace("/");
                },
            
                //socket IO version
                sendMsg() {
                  if (this.inputMsg.trim() === "") {
                    //空字符串直接return,trim在这里的作用是去除字符串前后的空格
                    return;
                  }
                  let msg = {
                    //存放信息
                    username: this.user.name,
                    content: this.inputMsg,
                  };
                  try {
                    this.mySocket.emit("msg", JSON.stringify(msg)); //把前端的信息发送给url里面
                    this.$message(this.inputMsg);
                  } catch (error) {
                    this.$notify(error);
                  }
                  this.inputMsg = "";
                },
            
                //------------
                //axios version
                startPolling() {
                  this.polling = window.setInterval(() => {
                    console.log("query (axios)");
                    this.getChats();
                  }, 1000);
                },
            
                addChat(chatObj) {
                  console.log(chatObj);
                  // this.messages.push(chatObj);
                },
            
                getChats() {
                  this.axios.get(this.backendUrl + "/chats").then((response) => {
                    console.log(response.data);
                  });
                  // $.get("/chats", (chats) => {
                  //   chats.forEach(addChat)
                  // })
                },
              },
            
              computed: {
                user() {
                  return this.$store.state.user;
                },
                mySocket() {
                  return this.$store.state.socket;
                },
              },
              destroyed() {
                if (this.polling) {
                  clearInterval(this.polling);
                }
              },
            };
            </script>
        • 服务端代码逻辑实现

          • var express = require('express')
            var app = express()
            var http = require('http').createServer(app)
            var io = require('socket.io')(http)//利用express在服务器端创建一个socket.io对象
            var path = require('path')
            
            const cors = require("cors");
            
            const corsOptions = {
                origin: '*',
                credentials: true,
                optionSuccessStatus: 200,
            }
            
            //解决一些跨域的问题
            app.use(cors(corsOptions))
            
            
            //通过express设置资源路径
            app.get('/', (req, res) => {
                res.setHeader('Access-Control-Allow-Origin', '*')
                res.sendFile(__dirname + '/index.html')
            })
            
            app.use(express.static(path.join(__dirname, '/')));
            
            
            //监听用户连接事件
            io.on('connection', (socket) => {
                console.log('---------- start ----------')
                socket.on('disconnect', () => {
                    console.log('---------- end ----------')
                })
                socket.on("msg", (msg) => {
                    console.log("message: " + msg);
                    io.emit('msg', msg)//把监听到的信息发送给所有的socket进程
                });
            
                socket.on("username", (name) => {
                    username = name;
                    console.log("> user [" + name + "] connected");
                });
            })
            
            http.listen('9999', () => {
                console.log('Listening on: 9999')//监听9999端口
            })
            
    • 总结与展望

      • 本次项目简单地通过Vue + Socket.io + Axios + Express实现了一个简易的在线多人聊天室,但是因为刚接触这些技术,难免有不成熟的地方,未来可以扩展到用户头像、图像传输、用户个人信息修改等等功能,将来有时间可以进行更进一步深入的学习之中!请多指教。

 

作者:NP208

 

 

 

...全文
743 回复 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

571

社区成员

发帖
与我相关
我的任务
社区描述
软件工程教学新范式,强化专项技能训练+基于项目的学习PBL。Git仓库:https://gitee.com/mengning997/se
软件工程 高校
社区管理员
  • 码农孟宁
加入社区
  • 近7日
  • 近30日
  • 至今

试试用AI创作助手写篇文章吧