学习node+websocket+vue3搭建多人聊天室

跟着做小白都能做出来~~

首先搭建前端Vite 项目

兼容性注意

Vite 需要 Node.js 版本 14.18+,16+。然而,有些模板需要依赖更高的 Node 版本才能正常运行,当你的包管理器发出警告时,请注意升级你的 Node 版本。

使用 npm或yarn、pnpm任意安装,个人推荐pnpm,原因如下

  • 快速:pnpm比传统方案(yarn, npm)安装包的速度快了两倍,甚至比yarn2,pnp模式还要快
  • 更严格高效:node_modules 中的文件是从一个单一的可内容寻址的存储中链接过来的,代码无法访问任意包
  • monorepo:天然内置支持当仓库多包


$ npm create vite@latest
$ yarn create vite
$ pnpm create vite

1.安装并输入项目名称

2.这里选择vue

3.根据个人选择,这里选择js

4.到这里就成功快速搭建完成vite3+vue3前端基础框架了,跟着下面的命令执行即可

在vsocde编辑器中打开

打开src目录,建立以下模块结构

首先cofigjs这里是用来统一管理地址的,后期维护也方便只需要修改地址即可

1.congfig-index.js中,这里写的是websokct

// 域名地址(项目实地址)这里写的是本地的地址
const BASE_URL='127.0.0.1'

// 端口号
const WS_PORT='8000'

// WebSocket协议的URL
export const WS_ADDRESS=`ws://${BASE_URL}:${WS_PORT}`

这里的hooks是vue3借鉴的react函数式组件的优点

2.hooks-index.js

// 这里的index.js文件用于统一引用导出hooks的方法。更规范
import  useWebSocket  from './websocket'
export { useWebSocket }

hooks-websocket.js

// 引用config-index.js封装好的WebSocket协议的URL
import { WS_ADDRESS } from "../configs"

// 封装一个使用websocket的高阶函数
// 这里的handleMessage用于接收及发送的消息
function useWebSocket(handleMessage) {

    // 建立连接(创建WebSocket对象):
    // const ws = new WebSocket(url, [protocol]);// url:服务器端地址;protocol:可选,指定可接受的子协议。
    const ws = new WebSocket(WS_ADDRESS)
    const init = () => {
        bindEvent()
    }
    
    init()

    function bindEvent() {
        // ws.onopen():连接建立时触发的事件
        ws.addEventListener('open', handleOpen, false);
        // ws.onclose():关闭连接方法
        ws.addEventListener('close', handleClose, false);
        // ws.onerror():通信发生错误时触发
        ws.addEventListener('error', handleError, false);
        // ws.onmessage(): 客户端接收服务器端发送的信息时触发
        ws.addEventListener('message', handleMessage, false);
    }
    function handleOpen(e) {
        console.log('WebSocket open', e)
    }
    function handleClose(e) {
        console.log('WebSocket close', e)
    }
    function handleError(e) {
        console.log('WebSocket error', e)
    }
    return ws
}
export default useWebSocket

3.pages-home.vue建立简单的聊天页面

<script setup>
// onMounted:挂载完数据
// reactive:响应式数据,reactive 参数必须是对象 (json / arr)
// toRaw:响应式转化普通对象
// toRefs:降reactive中的响应式结构为一个个的ref响应式数据
import { onMounted, reactive, toRaw, toRefs } from "vue";

// 引用全局路由对象useRouter
import { useRouter } from "vue-router";

// 引用hooks中封装好的高阶函数,用于消息发送
import { useWebSocket } from '../hooks'

// 这里useWebSocket(handleMessage)用于接收消息
const ws = useWebSocket(handleMessage)

const router = useRouter()

// 定义state用于接收和发送的消息
const state = reactive({
  msg: '',
  msgList: []
})

// 结构state
const { msg, msgList } = toRefs(state)

// 获取用户名,没有就跳转到登录页
let username = ''
onMounted(() => {
  username = localStorage.getItem('username')
  if (!username) {
    router.push('/login')
    return
  }
})

// 发送消息按钮
const sendMsg = () => {
  if (msg.length < 1) {
    return
  }
  // ws.send():发送信息的方法

  // 由于后台接收的是JSON类型,这里发送的消息需要JSON.stringify
  ws.send(JSON.stringify({
    id: new Date().getTime(),
    user: localStorage.getItem('username'),
    dateTime: new Date().getTime(),
    // 这里必须让msg失去响应式或深拷贝该值,否则造成发送的消息根据再次输入的消息发生变更
    msg: toRaw(msg.value),
  }))

  // 发送完清空消息框
  msg.value = ''
}
// 用于接收发送过来的消息
function handleMessage(e) {
  
  // 由于后台接收的是JSON类型,这里发送的消息需要JSON.parse
  const msgData = JSON.parse(e.data)

  // 向列表添加每条聊天消息
  msgList.value.push(msgData)
}
</script>

<template>
  <div>
    <ul>
      <li v-for="item in msgList" :key="item.id">
        <p>用户名:{{ item.user }}</p>
        <p>{{ new Date(item.dateTime) }}</p>
        <p>消息:{{ item.msg }}</p>
      </li>
    </ul>
    <input type="text" placeholder="请输入消息" v-model="msg">
    <button @click="sendMsg">发送</button>
  </div>
</template>

<style scoped>
li p {
  text-align: left;
}
</style>


pages-login.vue进入聊天室的页面

<template>
    <div>
        <input type="text" placeholder="请输入用户名" v-model="userName">
        <button @click="enterSocket">进入聊天室</button>
    </div>
</template>

<script setup>
import { onMounted, ref } from "vue";
import { useRouter} from 'vue-router'
const router=useRouter()
    const userName=ref('')
    onMounted(()=>{
        userName.value=localStorage.getItem('username')
        // 简单的判断是否已进入聊天室
        if(userName.value){
            router.push('/home')
            return
        }
    })
    // 进入聊天室
    const enterSocket=()=>{
        if(userName.value<6){
            alert('用户名不小于6位')
            return
        }
        localStorage.setItem('username',userName.value)
        userName.value=''
        router.push('/home')
    }
</script>

4.router-index.js 由于框架是没有自带router的这里需要去pnpm i vue-router安装下

import { createRouter, createWebHashHistory } from "vue-router";
import routes from "./router";
const router = createRouter({
    history: createWebHashHistory(),
    routes,
});
export default router;

router-router.js

import login from "../pages/login.vue";
// 路由列表
let routes= [
  {
    path: "/login",
    name: "login",
    component: login,
  },
  {
    path: "/home",
    name: "home",
    component: () => import("../pages/home.vue"),
    children: [],
  },
];
export default routes;

5.App.vue

<template>
  <router-view></router-view>
</template>

6.main.js

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from "./router";
createApp(App).use(router).mount('#app')

6.在当前目录下新建server目录,用于node服务

进入server目录生成package.json

然后安装包

在安装websocket的插件wx

接着是node的目录如一下就完成

在package.json中配置启动命令如

index.js中写

// 使用CommonJS require引入
// CommonJS 中的 require/exports 和 ES6 中的 import/export 区别
// CommonJS 模块是运行时加载,ES6 Modules 是编译时加载并输出接口。
// CommonJS 输出是值的拷贝;ES6 Modules输出的是值的引用,被输出模块的内部的改变会影响引用的改变。
// CommonJs 导入的模块路径可以是一个表达式,因为它使用的是 require() 方法,甚至这个表达式计算出来的内容是错误的路径,也可以通过编译到执行阶段再出错;
// 而ES6 Modules 只能是字符串,并且路径不正确,编译阶段就会抛错。
// CommonJS this 指向当前模块,ES6 Modules this 指向 undefined
// ES6 Modules 中没有这些顶层变量:arguments、require、module、exports、__filename、__dirname
const Websocket=require('ws')
;((Ws)=>{
    // 建立ws服务地址,这里跟前端都类似了唯一的区别是connection和message(连接和消息)
    const server=new Ws.Server({port:8000,host:"0.0.0.0"})
    const init=()=>{
        bindEvent()
    }
    function bindEvent(){
        server.on('open',function(event){
            server.send('OpenBarScanner')
        })
        server.on('close',handelClose)
        server.on('error',handelError)
        server.on('connection',handelConnection)
    }
    function handelOpen(){
        console.log('handelOpen')
    }
    function handelClose(){
        console.log('handelClose')

    }
    function handelError(){
        console.log('handelError')

    }
    // 连接时提示消息并发送到客户端
    function handelConnection(ws){
        console.log('WebSocket Connection')
        ws.on('message',handelMessage)
    }
    // 用于接收发送的消息
    function handelMessage(msg){
        server.clients.forEach((c)=>{
            // 由于node发送到客户端的消息是Blob类型,这里需要用Buffer.form().toString()转化为utf8类型识别消息
            c.send(Buffer.from(msg).toString('utf8'))
        })
    }
    init()
})(Websocket)


点赞2
点击评论1
收藏1
浏览 82
 

还没有评论,快来发表第一个评论吧

免责声明:凡在本网站出现的信息,均仅供参考。本网站将尽力确保所提供信息的准确性及可靠性,但不保证有关资料的准确性及可靠性,注册用户和一般页面游览者在使用前请进一步核实,并对任何自主决定的行为负责。本网站对有关资料所引致的错误、不确或遗漏,概不负任何法律责任(包括侵权责任、合同责任和其它责任)
*尊重作者,转载请注明出处!