VUE3+NODE开发一个简易的聊天室(2)

461 阅读3分钟

前端部分

整理好了实现聊天室的思路与要实现的功能,开始写代码。

首先用Vue-Cli脚手架搭建项目,网上教程很多,这里就不阐述了。

采用了VUE3+Ts+Element plus的技术栈。

这里为审美比较差的前端朋友们推荐两个网站(没有UI真是难受)。

渐变色:uigradients

圆角:neumorphism.io

页面主要为登录页与聊天页,主色调为黑白,这里就不放代码了,页面很简单,基本上就是以下几个部分。

登录页 image.png 借鉴了codepen的大神,原链接在此

聊天页 image.png

创建/加入房间 image.png

image.png

房间详情 image.png

实现好了页面,将api部分封装好,就可以愉快地调接口啦!使用的是老搭档axios,定义好请求拦截器、接收拦截器、处理好异常返回即可,Po出部分代码。

const service = axios.create({
    baseURL:process.env.VUE_APP_BASE_URI,//请求地址区分本地与线上环境
    withCredentials:false,//跨域
    timeout:3*60*1000,//请求超时
});
//请求拦截器
service.interceptors.request.use((config:any) => {
    //在这里对请求头添加token
    return config;  //  有且必须有一个config对象被返回
 }, error:any => {
    //   对请求错误做些什么
     return Promise.reject(error);
 });
 //接收拦截器
 service.interceptors.response.use(
    response => {
        //处理回调
        if (response.data.state == 0) {
            return Promise.resolve(response.data);
        } else {
            return Promise.reject(response);
        }
    },
    error => {
        console.log("服务器错误")
    }
);

采用websocket实现实时通讯,需要封装websocket部分,主要分为心跳、发送、接收以及各种异常处理,做了很多console方便出错时定位错误。

心跳

let heart = {
        timeOut: 5000,
        timeObj: null,
        serverTimeObj: null,
        start: function () {
            // console.log("heart start");
            let self = this
            self.timeObj && clearTimeout(self.timeObj);
            self.serverTimeObj && clearTimeout(self.serverTimeObj);
            self.timeObj = setTimeout(function () {
                if (!websock) {
                    console.log("%c通信连接已经关闭或不可用 " + nowTime(), "color:red")
                    return
                }
                if (websock.readyState == 0) {
                    console.log("%c通信连接未建立,建议重新连接 " + nowTime(), "color:yellow")
                }
                if (websock.readyState == 1) {
                    console.log("%c心跳正常 " + nowTime(), "color:green")
                }
                if (websock.readyState == 2) {
                    console.log("%c通信连接正在关闭 " + nowTime(), "color:yellow")
                }
                if (websock.readyState == 3) {
                    console.log("%c通信连接已经关闭或不可用 " + nowTime(), "color:red")
                }
                websock.send(JSON.stringify({ text: 'websocket心跳检测' + nowTime() }));//发送消息,服务端返回信息,即表示连接良好,可以在socket的onmessage事件重置心跳机制函数
                //定义一个延时器等待服务器响应,若超时,则关闭连接,重新请求server建立socket连接
                self.serverTimeObj = setTimeout(function () {
                    reConnect();
                }, self.timeOut)
            }, self.timeOut)
        }
    }

初始化socket

const initSocket = ()=>{
    if (websock) {
        if (websock.readyState == 1) {
            console.log("%c可正常通信,无需重连 " + nowTime(), 'color:green')
        } else {
            websocketclose()
        }
    }
    //socket地址需要传入当前的房间号,方便区分推送聊天消息
    websock = new WebSocket("ws://"+process.env.VUE_APP_BASE_SOCKET_URI+"/"+props.roomCode);
    websock.onmessage = websocketonmessage;
    websock.onerror = websocketonerror;
    websock.onopen = () => {
        websocketonopen()
    };
}

其他

const websocketonopen = () => { //连接建立之后开始心跳
    console.log("%c连接成功 " + nowTime(), 'color:green')
    heart.start();
}
const websocketonerror = () => {//连接建立失败重连
    console.log("%c连接异常,尝试重新连接", "color:red")
    reConnect();
}
const websocketclose = ()=>{//离开房间后需要关闭socket
    if (websock) {
        websock.close()
        websock = null
    }
    console.log('%c连接关闭 ' + nowTime(), "color:red");
}
const reConnect = () => {//自动重连
    websocketclose()
    if (reConnectCount == 3) {//可自行定义重连次数,reConnectCount需要全局定义
        console.log("%c重连失败已达3次,连接关闭 " + nowTime(), "color:red")
        reConnectCount = 0
        return false
    }
    setTimeout( () =>{
        reConnectCount++
        initSocket();
    }, 1000)
}
const websocketonmessage = (e: any) => { //数据接收
    console.groupCollapsed("%cwebcosket接收数据" + nowTime(), "color:grey");
    console.log(e.data);
    console.groupEnd();
    // 这里处理数据,使用type来区分接收到的数据是消息还是心跳返回
    if(JSON.parse(e.data).type == 1){
        //---------------------------------------
        //处理接收的数据,渲染到页面上
        //---------------------------------------
        //接收到数据后,如果页面消息堆叠过多,需要自动将页面滚动至底部
        proxy.$nextTick(()=>{
            var ele = document.getElementById(id);
            //判断元素是否出现了滚动条
            if(ele.scrollHeight > ele.clientHeight) {
                //设置滚动条到最底部
                ele.scrollTop = ele.scrollHeight;
            }
        })
        //---------------------------------------
    }
    //继续心跳
    heart.start();
}

连接成功效果

image.png