Vue 使用 Vue-socket.io 实现即时聊天应用(实战篇 二)

Gitee源码:https://gitee.com/wfeng0/CSDN

GitHub源码:GitHub - wf0/CSDN: vue-socket.io实现即时聊天

视频介绍:vue-socket.io实现即时聊天_哔哩哔哩_bilibili


目录

1.项目说明:

2.登录页面实现:

 3. 连接socket.io:

3.1 下载vue-socket.io:

3.2 引用:

4. Node服务器的开发:

4.1新建一个文件夹

 4.2 安装依赖

 4.3 新建入口文件index.js

4.4 启动服务,测试连接 

4.5 监听用户登录

 4.6 将用户名加到socket实例的属性上,便于我们处理私聊。

 4.7 监听服务器是否存在用户:

5.  动态渲染用户列表

5.1 使用vuex处理用户列表

 5.2 vuex管理socket事件

 5.3 默认群聊的实现

5.4 服务器管理在线人数(用户列表) 

6. 群聊的实现

6.1 发送消息到服务器:

6.2 渲染聊天列表:

7. 私聊的实现

8. 实现效果

 9. 总结:

9.1 前端UI设计

9.2 socket服务器的设计

9.3 群聊的实现

9.4 私聊的实现

9.5 项目难点


1.项目说明:

技术讨论群【522121825】

我们主要通过这个目,练习一下vue-socket.io的群聊私聊两个功能,至于房间Rooms,有兴趣的伙伴可以自己去研究啦。我是将整个打代码、思考的过程写了下来,如果只想要结果的,可以直接看我的总结部分,或者到我的GitHub主页上看源码。还是希望大家好好看,相信会有收获的。好了,直接开始吧。

那我们的群聊是怎么实现的呢?我们会设计一个登录页面,要求输入用户的用户名,作为聊天应用的唯一标识。群聊是登录上来就有一个默认的群,而每个用户都能触发私聊。就这个思路,下面来实现。

2.登录页面实现:

//vuex 
state: {
        /* 记录登录状态 */
        isLogin:false,
}

//App.vue
 computed:{
    isLogin(){
      return store.state.isLogin;
    },
//我们通过获取vuex的数据,判断用户是否登录

使用 v-if  v-else实现登录的控制:

<!-- 登录 -->
<div v-if="!isLogin">
     
</div>
<!-- 聊天 -->
<el-container v-else>
    
</el-container>

效果图如下:

 用户输入用户名,并选择头像后,点击登录进入系统;具体的代码如下(我就不分解详细说了,因为页面的样式每个人都有自己的风格,如果对我的代码不懂的,可以留言讨论呢。

<!-- 登录 -->
    <div v-if="!isLogin" class="login">
      <el-tabs v-model="activeName">
        <el-tab-pane label="欢迎登录" name="first">
          <!-- 用户名输入 -->
            <el-input v-model="username" placeholder="请输入用户名">
              <el-button slot="append">登录</el-button>
            </el-input>
            <!-- 头像选择 -->
            <div class="avatar">
              <span @click="avatar(src)" v-for="(src,index) in avatarList" :key="index">
                <el-avatar :src="src" :class="{'choosed':src==choosed}"></el-avatar>
              </span>
            </div>
        </el-tab-pane>
      </el-tabs>
    </div>
 data() {
    return {
      activeName:'first',
      username:'',
      choosed:'',
      avatarList:[
        'http://img.mp.itc.cn/upload/20170808/5861bc790e654d56bc9289c567b44875_th.jpg',
        'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png',
        'http://gss0.baidu.com/-fo3dSag_xI4khGko9WTAnF6hhy/zhidao/pic/item/30adcbef76094b36ba49777aa5cc7cd98c109d49.,jpg',
        'http://img.52z.com/upload/news/image/20180111/20180111085521_86389.jpg',
        'http://img.mp.itc.cn/upload/20170808/5861bc790e654d56bc9289c567b44875_th.jpg',
        'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png',
        'http://gss0.baidu.com/-fo3dSag_xI4khGko9WTAnF6hhy/zhidao/pic/item/30adcbef76094b36ba49777aa5cc7cd98c109d49.,jpg',
        'http://img.52z.com/upload/news/image/20180111/20180111085521_86389.jpg'
      ],
    }
  },

css:

.choosed{
  border:  solid 1px red;
}
.login{
  width: 50%;
  margin-left: 25%;
  border: solid 1px rgb(228, 231, 237);
  padding: 30px;
}
.login .avatar{
  margin-top: 20px;
}
.login .avatar .el-avatar{
  cursor: pointer;
}

也可以自己写这个登录页,确保用户输入用户名和头像信息。

 业务就是将选中的头像赋给choosed;就能应用css了:(被选中的头像加了边框

 这里注意啊,这个信息是你!你!!所以,要将这个信息告诉vuex,修改myInfo的数据。(是登录按钮的事件处理,同时,还要连接socket.io

//vuex:
setMyInfo(state,data){
            state.myInfo=data;
            state.isLogin=true;
        },
//App login事件:
login(){
      if(this.username&&this.choosed){
        /* 告诉vuex修改个人信息 */
        store.commit('setMyInfo',{
          img:this.choosed,
          name:this.username,
        });
        /* 连接socket */
      }
    },

现在能跳过去,但是我们的信息并没有修改,原因是我们的数据是定死的,现在要监听vuex数据;

 <!-- 我的信息 -->
      <div class="myinfo">
        <el-avatar src="http://img.52z.com/upload/news/image/20180111/20180111085521_86389.jpg"></el-avatar>
        <span>在风中飞翔~</span>
      </div>

 如下:

!-- 我的信息 -->
      <div class="myinfo">
        <el-avatar :src="myInfo.img"></el-avatar>
        <span>{{myInfo.name}}~</span>
      </div>

//监听vuex数据
 computed:{
    myInfo(){
      return store.state.myInfo;
    },
  },

 现在就能实现了:

 3. 连接socket.io:

终于到了我们的关键技术了!前面的所有都是铺垫,为了页面好看些。

3.1 下载vue-socket.io:

npm i vue-socket.io --s

3.2 引用:

(这里要关闭自动连接!不然造成node服务器资源浪费。服务器地址我们后面再写)

import VueSocketIO from 'vue-socket.io'
//我直接use在后面了,你也可以新起一行
Vue.use(ElementUI).use(
  new VueSocketIO({
    debug: true, // debug调试,生产建议关闭
    connection: "url",
    options: {     //Optional options, 
      autoConnect:false, //关闭自动连接,在用户登录后在连接。
    }
  })
);

4. Node服务器的开发:

4.1新建一个文件夹

跟你的项目同级,不建议将服务器的文件夹建在vue项目里面,这样代码容易混乱。(空的,就是普通的文件夹)

 4.2 安装依赖

npm i socket.io http express file --s

 所需的依赖是:socket.io、http、express、file(用空格隔开,就能安装多个)

 等你装完,目录就自己有了!

 4.3 新建入口文件index.js

这个名字可以随意。

服务器连接的代码可以参考我的这篇文章:Vue 使用 Vue-socket.io 实现即时聊天应用(连接篇)

 包括请求跨域,400错误的解决办法等;下面是简单的代码,(这里可以打开自动连接,看能不能连接上。我们现在只需要socket.io服务器,所以我没有写http的服务,http的服务,是有app.get(....)的。要区分socket服务和http服务

var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http,{
    allowEIO3: true,
    cors: {
          origin: "http://localhost:8080",
          methods: ["GET", "POST"],
          credentials: true
      }
  });
 
io.on('connection', function(socket){
  console.log('a user connected');
});
 
http.listen(3000, function(){
  console.log('listening on *:3000');
});

4.4 启动服务,测试连接 

这个步骤是一定要做的,测试连接,建议先打开自动连接,没问题了关闭。我的没问题:

4.5 监听用户登录

(以后的事件,都定义在io中,我就不再全部写了)

io.on('connection', function (socket) {
    /* 监听用户登录事件 */
    socket.on('login',data=>{
        console.log('用户登录:',data);
    });
    console.log('a user connected');
});

 App.vue:记得main.js中关闭自动连接啊啊啊啊!!

login(){
      if(this.username&&this.choosed){
        /* 告诉vuex修改个人信息 */
        store.commit('setMyInfo',{
          img:this.choosed,
          name:this.username,
        });
        /* 连接socket */
        this.$socket.connect();
        this.$socket.emit('login',{name:this.username});
      }
    },

现在应该是监听了,但是还没有连接,用户登录了再连接成功:

 

 4.6 将用户名加到socket实例的属性上,便于我们处理私聊。

可以参考我的文章:Vue 使用 Vue-socket.io 实现即时聊天应用(通讯篇)(服务器的console.log对用户影响不大,但是对我们调试程序却很有用,不建议删除)

/* 监听用户登录事件,并将数据放到socket实例的属性上 */
    socket.on('login',data=>{
        console.log('用户登录:',data);
        socket.name=data.name;
    });

思考:socket.name应该唯一吗?既然是私聊的唯一标识,一定是唯一的!!不然私聊找到两个人了。因此,登录的时候,不是简单的连接就行了,还要判断服务器上是否存在已有该用户名!这个才是关键!建议使用最简单的回调函数解决。

App.vue:(不能写具名函数,使用箭头函数)

this.$socket.emit('login',{name:this.username},(result)=>{
    console.log(result)
});

node.js: 

 /* 监听用户登录事件,并将数据放到socket实例的属性上 */
    socket.on('login',(data,callback)=>{
        console.log('用户登录:',data);
        socket.name=data.name;
        callback(123);
    });

 接收到回调的数据:

可以看官网的描述:

 4.7 监听服务器是否存在用户:

如上所诉,需要监听服务器是否存在用户,使用回调函数处理,是最简单的。App根据返回的数据,决定登录成功还是给出错误提示:

/* 监听用户登录事件,并将数据放到socket实例的属性上 */
    socket.on('login',(data,callback)=>{
        /* 遍历服务器连接对象 */
        var islogin=true;
        io.sockets.sockets.forEach(iss => {
            if(iss.name==data.name){
                islogin=false;
            }
        });
        if(islogin){
            console.log('用户登录成功:',data);
            socket.name=data.name;
            callback(true);
        }else{
            console.log('用户登录失败!:',data);
            callback(false);
        }
    });

 App根据返回的数据做判断:

5.  动态渲染用户列表

哈哈哈,一个登录就这么多事!坚持下去啊,后面就很快了。

 现在实现动态渲染用户列表:

5.1 使用vuex处理用户列表

 我们的数据,现在还是Aside管理,现在改为vuex管理,放在userList中,同时!还要在从vuex取出来渲染。

state: {
       //....
        /* 用户列表 */
        userList:[
            {
                name: '王小虎',
                img: 'http://img.mp.itc.cn/upload/20170808/5861bc790e654d56bc9289c567b44875_th.jpg'
              }, {
                name: '郑泷',
                img: 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png'
              }, {
                name: '小蛮',
                img: 'http://gss0.baidu.com/-fo3dSag_xI4khGko9WTAnF6hhy/zhidao/pic/item/30adcbef76094b36ba49777aa5cc7cd98c109d49.jpg'
              }, {
                name: '张云',
                img: 'http://img.52z.com/upload/news/image/20180111/20180111085521_86389.jpg'
              }
        ],
    },

在Aside中,不需要定义数据了,而是直接计算出来;(就是原来tableDate是data数据,现在直接用computed)

<script>
import store from '../store/index'
export default {
  data() {
    return {
      keyword: "",
    };
  },
  methods: {
    setUserInfo(row, column, event){
      store.commit('setUserInfo',{name:row.name,img:row.img});
    },
  },
  computed:{
    tableData(){
      return store.state.userList;
    },
    myInfo(){
      return store.state.myInfo;
    },
  },
};
</script>

确保用户列表渲染不变!!

 5.2 vuex管理socket事件

动态渲染,用户登录上来要更新列表:

使用vuex管理:(可以结合Vue 使用 Vue-socket.io 实现即时聊天应用(vuex管理)_~朴:shu的博客-CSDN博客 这篇博客看看)

Vue.use(ElementUI).use(
  new VueSocketIO({
    debug: true, // debug调试,生产建议关闭
    connection: "http://localhost:3000",
    vuex: {
      store,
      actionPrefix: 'SOCKET_',
      mutationPrefix: 'SOCKET_'
    },
    options: {     //Optional options, 
      autoConnect:false, //关闭自动连接,在用户登录后在连接。
    }
  })
);

vuex:

 /* 渲染用户列表 */
        SOCKET_login(state,data){
            console.log('vuex',data)
        },

服务器:

if(islogin){
            console.log('用户登录成功:',data);
            socket.name=data.name;
            callback(true);
            io.emit('login',data)
        }else{
            console.log('用户登录失败!:',data);
            callback(false);
        }

都是login事件,大家区分清楚啊!建议:可以将socket的生产提示关了。

 

 收到的数据还差头像信息!!在login事件中传过去:

 默认为空的,收到数据后,push到userList中

/* 渲染用户列表 */
SOCKET_login(state,data){
    state.userList.push(data)
},

通知所有连接的用户,有人连接上来了,不然没有效果嘿嘿!通知vuex说有人连接上来,应该用的广播才行!!!

socket.emit('login',data)=>io.emit('login',data)  一个是连接实例,一个是广播!

实现效果:

当二号用户连接上来的时候,一号会监听vuex的login事件(是广播的!!)

所以,他的页面是这样:

 已经成功一半了!坚持呀。

 5.3 默认群聊的实现

加一个默认的群聊,

/* 用户列表 */
        userList:[
            {
                name:'默认群聊',
                img:'https://pic1.zhimg.com/50/v2-adfacac8307b48531d4e341a6090aa03_hd.jpg?source=1940ef5c'
            }
        ],

 效果如下:

保证每个人进来,都有一个默认的群聊。(同时,你还可以优化一下,使用 数据过滤实现判断该用户是不是你,是你就不显示) 

 <el-table @cell-click="setUserInfo" 
:data="tableData.filter(data => (!keyword||
(data.name.toLowerCase().includes(keyword.toLowerCase())))
&&
!(data.name.toLowerCase().includes(myInfo.name.toLowerCase())))" 

 数据过滤:(为空字符不过滤   ||   过滤数据中include(包含)keyword)    &&    过滤掉自己(取非了)

 

这样子好多了! 

 但是不知道大家有没有发现一个问题!为什么后面登录的就没有前面的人的消息?1号是先登录的,2号是后登录的,每次登录进系统,都会从vuex获取用户列表,列表都是空的,原因在这里!解决办法是通过socket服务器返回在线人数是最准确,最及时的。

5.4 服务器管理在线人数(用户列表) 

/* 接收在线人数,传给前端,保证在线人数是最新的 */
var userList=[];
//...
if(islogin){
    console.log('用户登录成功:',data);
    userList.push(data);
    socket.name=data.name;
    callback(true);
    io.emit('login',userList);
}else{
    console.log('用户登录失败!:',data);
    callback(false);
}

 传过去数组,就不用push啦,直接在vuex中赋值:

/* 渲染用户列表 */
        SOCKET_login(state,data){
            state.userList=data;
        },

 

在服务器端添加默认群聊:

/* 接收在线人数,传给前端,保证在线人数是最新的 */
var userList=[
    {
        name:'默认群聊',
        img:'https://pic1.zhimg.com/50/v2-adfacac8307b48531d4e341a6090aa03_hd.jpg?source=1940ef5c'
    }
];

同时,对于掉线的用户,我们直接从数组中删除,然后再传给前端即可:(注意啊,一定要添加条件,不然返回 -1,会把默认群聊也删除了

 /* 用户掉线 */
    socket.on('disconnect',()=>{
        console.log('用户离开')
        /* 删除用户 */
        let index=userList.findIndex(i=>i.name==socket.name);
        if(index){
            userList.splice(index,1);
            /* 通知前端 */
            io.emit('login',userList);
        }
    });

效果如下:

 到这里,已经可以实现群聊和私聊了!

6. 群聊的实现

 实现群聊需要两个步骤,1.发送消息到服务器,并广播给所有连接实例,2. 渲染聊天列表(难点);

6.1 发送消息到服务器:

send(){
      /* 发送消息 */
      /* 先判断是群聊还是私聊 */
      this.$socket.emit('groupChat',{});
      this.$socket.emit('privateChat',{});
      console.log(this.input);
      /* 清空输入框 */
      this.input='';
    },

 所以,Footer组件需要一个数据,根据Aside点击的是群聊还是私聊。使用vuex管理这个数据:(vuex文件)

/* 聊天类型 */
chatType:'',

 /* 修改聊天类型 */
changeChatType(state,data){
    state.chatType=data;
},

 我们通过Aside列表来渲染用户名,所以,在这里修改聊天类型:

setUserInfo(row, column, event){
      if(row.name=='默认群聊'){
        store.commit('changeChatType','group');
      }else{
        store.commit('changeChatType','private');
      }
      store.commit('setUserInfo',{name:row.name,img:row.img});
    },

 Footer组件监听:

 methods: {
    send(){
      /* 发送消息 */
      /* 先判断是群聊还是私聊 */
      if(this.chatType=='group'){
        this.$socket.emit('groupChat',{});
        console.log('群聊');
      }else{
        this.$socket.emit('privateChat',{});
        console.log('私聊');
      }
      console.log(this.input);
      /* 清空输入框 */
      this.input='';
    },
  },
  computed:{
    chatType(){
      return store.state.chatType;
    },
  },

 能识别后,就触发不同的事件即可。

if(this.chatType=='group'){
        this.$socket.emit('groupChat',{
          username: this.userInfo.name,
          list:{
              type: "my",//标记是我发的信息,但是通过服务器转发,必须是user,变成别人的,才能让别人渲染成功,不然所有人发送,都是my,就都在右边了
              time: time.toLocaleString( ), //获取日期与时间,
              msg: this.input,
            }
        });
      }

一定要注意这个type。要服务器发给(除了自己)的客户端,并且修改类型为user!自己的信息就push到数组中就行了:

6.2 渲染聊天列表:

还是回到我们上一篇写的最后的难点:数据结构

 /* 聊天记录 */
        chatMessageList:[{
            username: "默认群聊",
            list: [
              {
                type: "my",
                time: "",
                msg: "你好啊",
              },
              {
                type: "user",
                time: "",
                msg: "你好啊",
              },
              {
                type: "user",
                time: "",
                msg: "你好啊",
              },
            ],
          },
        ],

我发的消息对于别人是 user,对于自己是my,不然没有左右的效果,我们在上一篇的时候,没有考虑群聊,我们应该在每一条消息中,都加入用户头像信息才行:

 因此,修改数据传输,数据都是vuex监听得到的(computed):

/* 先判断是群聊还是私聊 */
      if(this.chatType=='group'){
        var data={
          username: this.userInfo.name,
          list:{
            name:this.myInfo.name,
            img:this.myInfo.img,
            type: "my",//标记是我发的信息,但是通过服务器转发,必须是user,变成别人的,才能让别人渲染成功,不然所有人发送,都是my,就都在右边了
            time: time.toLocaleString( ), //获取日期与时间,
            msg: this.input,
          }
          }
        this.$socket.emit('groupChat',data);
        /* 自己的信息直接push到数组中 */
        store.commit('SOCKET_updateChatMessageList',data);
      }

 vuex处理数据:(这个也是难理解的)

 /* 聊天记录的修改,这里我们使用vuex监听 */
        SOCKET_updateChatMessageList(state,data){
            var finduser=false;
            /* 数据处理:先找到自己的聊天记录 */
            state.chatMessageList.forEach(list=>{
                if(list.username==data.username){
                    finduser=true;
                    list.list.push(data.list)
                }
            });
            if(!finduser){
                state.chatMessageList.push({
                    username:data.username,
                    list:[data.list]
                });
            }
        },
//为什么要自己封装这个数据,因为传过来的list是对象,需要封装为数据,便于下次直接push进去

 主要是数据结构理解了就好了

 服务器监听群聊事件,现在是没有做处理的,直接转发,会有一个严重的问题:

/* 监听群聊事件 */
    socket.on('groupChat',data=>{
        // 发送给所有客户端,除了发送者
        socket.broadcast.emit('updateChatMessageList',data);
    });

 

 我群聊发的消息,在你页面显示是你的消息!为啥?因为我们定义type是my!所以服务器转发,需要处理这个小问题。

/* 监听群聊事件 */
    socket.on('groupChat',data=>{
        // 发送给所有客户端,除了发送者
        /* 修改源数据的属性 */
        data.list.type='user';
        socket.broadcast.emit('updateChatMessageList',data);
    });

 这样,我发的,你就是收的。还有头像没有处理呢,在数据传输的时候加上头像信息。修改main循环的内容:

群聊实现了! 主要理解数据结构,怎么处理数据就好了。(后面的私聊消息的处理,也是这个数据结构)

7. 私聊的实现

我们还是直接使用io.sockets.sockets找人,你也可以修改代码,将socket.id放在在线人数的数组中。

实现了群聊,私聊的代码非常简单:

 /* 监听私聊事件 */
    socket.on('privateChat',data=>{
        /* 找到对应的私聊对象 */
        io.sockets.sockets.forEach(iss=>{
            if(iss.name==data.username){
                data.list.type='user';
                io.to(iss.id).emit('updateChatMessageList',data);
            }
        });
    });

//我们把data定义到外面了,数据结构相同

/* 先判断是群聊还是私聊 */
      if(this.chatType=='group'){
        this.$socket.emit('groupChat',data);
        /* 自己的信息直接push到数组中 */
        store.commit('SOCKET_updateChatMessageList',data);
      }else{
        this.$socket.emit('privateChat',data);
        /* 自己的信息直接push到数组中 */
        store.commit('SOCKET_updateChatMessageList',data);
      }

 我给你发,你收不到,你发我也收不到!问题出在我们的数据结构!我们循环遍历的是聊天对象名字,2号给1号发消息,看头上是1!但是,1号想收到消息,头上是2!!

为了让大家明白这个,我先把过滤关了:

 

现在是两个用户,22给1发消息,注意头上的标记:

 1号想收消息,是这样:

 但是没有收到!因为数据到头顶是1的人那里了:

这数据结构设计有问题!!! 再认真看一下我们的数据结构:

 chatMessageList:[{
            username: "默认群聊",
            list: [
              {
                type: "my",
                time: "",
                msg: "你好啊",
              },
              {
                type: "user",
                time: "",
                msg: "你好啊",
              },
              {
                type: "user",
                time: "",
                msg: "你好啊",
              },
            ],
          }
        ],

 现在问题应该清晰了,username不能直接用用户名做标记。默认群聊是因为所有人都是这个名字!(要求,私聊双方看到的是一样的!使用房间可以解决,但是很浪费资源,不推荐。

因此数据结构设计不合理?百度找办法,看看别人怎么设计的......(根据发送者与接收者作为区分,群聊的话,固定接收者是’默认群聊‘即可)

 chatMessageList:[{
            sender:'',//发送者id
            receiver: '',//接收方id
            time:'',//发送时间
            msg:''//消息内容
           }],

因为数据结构变了,需要改变的地方挺多哦,我一一说:

循环遍历的地方:

//Mian.vue

 <!-- 先循环找到你想要聊天的那个人 -->
    <div v-for="(list, index) in msgList" :key="index">
      <!-- 有聊天记录:循环聊天记录 -->
      <div v-if="(list.receiver==userInfo.name)&&userInfo.name=='默认群聊'">
        <!-- 再循环显示聊天记录 --> 
        <p :class="{'right':list.type=='my'}">
          <el-avatar v-if="list.type=='user'" :src="list.senderimg"></el-avatar>
          <el-avatar v-if="list.type=='my'" :src="list.senderimg" style="float:right;"></el-avatar>
          <span class="content">{{list.msg}}</span>
        </p>
      </div>
      <div v-else>
        <!-- 根据接收者和发送者定位聊天记录 -->
        <div v-if="((list.receiver==myInfo.name)&&(list.sender==userInfo.name))||((list.receiver==userInfo.name)&&(list.sender==myInfo.name))">
            <!-- 再循环显示聊天记录 --> 
            <p :class="{'right':list.type=='my'}">
              <el-avatar v-if="list.type=='user'" :src="list.senderimg"></el-avatar>
              <el-avatar v-if="list.type=='my'" :src="list.senderimg" style="float:right;"></el-avatar>
              <span class="content">{{list.msg}}</span>
            </p>
        </div>
      </div>

条件就是发送者是我,接收者是用户,或者接收者是我,发送者是用户,这样就能包括所有可能了。

那传到服务器的数据也要变啦:(群聊默认receive是'默认群聊')

send(){
      var time = new Date();
      /* 发送消息 */
      /* 先判断是群聊还是私聊 */
      if(this.chatType=='group'){
         let data={
            type:'my',
            sender:this.myInfo.name,//发送者id
            senderimg:this.myInfo.img,//发送者的img
            receiver: '默认群聊',//接收方id
            time:time.toLocaleString( ),//发送时间
            msg: this.input,//消息内容
        }
        this.$socket.emit('groupChat',data);
        /* 自己的信息直接push到数组中 */
        store.commit('SOCKET_updateChatMessageList',data);
      }else{
         let data={
            type:'my',
            sender:this.myInfo.name,//发送者id
            senderimg:this.myInfo.img,//发送者的img
            receiver:this.userInfo.name,//接收方id
            time:time.toLocaleString( ),//发送时间
            msg: this.input,//消息内容
        }
        this.$socket.emit('privateChat',data);
        /* 自己的信息直接push到数组中 */
        store.commit('SOCKET_updateChatMessageList',data);
      }
      /* 清空输入框 */
      this.input='';
    },

vuex监听变简单了,因为类型就是对象,所以直接push:

 /* 聊天记录的修改,这里我们使用vuex监听 */
        SOCKET_updateChatMessageList(state,data){
            state.chatMessageList.push(data);
        },

服务器监听事件注意属性变化:data.list.type变成data.type ,找人的name变了:变成receive:

/* 监听私聊事件 */
    socket.on('privateChat',data=>{
        /* 找到对应的私聊对象 */
        io.sockets.sockets.forEach(iss=>{
            if(iss.name==data.receiver){
                data.type='user';
                io.to(iss.id).emit('updateChatMessageList',data);
            }
        });
    });

8. 实现效果

我打开了3个窗口:

 开始群聊:

 私聊:

 3号的聊天窗口是空的,模拟1号与3号聊天,2号是空的。完全符合:

 9. 总结:

 终于将这个难关攻克下来了!过程虽然一直出错,但是我想将这个过程分享给大家,而不是写完美的结果,不然我踩过的坑,大家有可能继续,而且也想引发大家的思考。通过这个小项目,对我印象深刻!让我对vue-socket.io的使用以及期间的连接、管理等多维度的知识有了一定掌握,希望对大家有帮助!总结一下这个项目吧。

9.1 前端UI设计

前端UI设计我就不多说了,每个人都有不同的风格,但是我们要模拟登录页面,也是练习了socket.io的连接问题(autoConnect:false);同时,通过用户输入的username,绑定socket实例,便于我们私聊找人。

9.2 socket服务器的设计

现在看来,socket服务器的设计应该是最简单的。无非就是在需要的时候触发事件,在需要的时候监听事件就好了,不涉及难点。这只是小项目,真正的开发,可能需要node结合数据库,将聊天内容保存下来。(大家可以试试)

9.3 群聊的实现

这个项目中用到了两处广播:一个是用户列表的管理。通过服务器管理列表是最准确的,同时对前端也更加友好,不需要我们去处理更多的用户数据。还有的就是群聊,群聊就是广播了。不知道大家注意到没有,用户列表使用的是  io.emit('xxx',data) 而我们群聊发送消息是用的socket.broadcast.emit('broadcast', 'hello friends!');( // 发送给所有客户端,除了发送者),因为群聊设计左右的消息布局,我们在开始设计的时候,就这样啦。所以我们还需要手动的 store.commit()去修改自己的消息。其实,还有更好的办法解决。后面,我们的数据结构变了之后,可以直接根据sender是不是自己,来判断放在右边。这样,可以直接使用  io.emit(),省去很多麻烦。

9.4 私聊的实现

私聊的设计,主要是数据结构卡了一下,其实私聊还是比较简单的。主要是服务器如何找到对应私聊的那个人。我们使用了数组接收在线人数,其实也应该直接将socket.id放在数组中,这个倒是问题不大。两个方式实现难度不大。主要是你对原理理解多少啦。

9.5 项目难点

整个项目的难点,就是聊天数据结构的设计。一个好的数据结构,可以让你的项目简单好几倍!!!我深刻感受到。

9.6 项目的改进

这个项目目前已经有雏形了,但是还不是很完善,比如聊天信息自动置底,聊天应该加名称等等,有兴趣的小伙伴可以在项目基础上修改,欢迎大家提建议。

总之,这个项目做完啦,记录下来,希望对大家有所帮助。

真心不易,快两万字了。呜呜~~

Vue2使用vue-socket.io可以实现Socket.io的集成,实现实时聊天等功能。首先,需要在项目中引入vue-socket.io扩展,可以通过npm安装vue-socket.io并引入。 在Vue实例中,使用Vue.use()来注册并使用Vue-socket.io。在注册时,需要传入相应的配置参数,包括debug、connection、vuex等。其中,debug用于调试,可以选择关闭;connection用于指定Socket.io的连接地址;vuex用于在Vuex中使用Socket.io的事件监听。 具体实现步骤如下: 1. 首先,安装vue-socket.io扩展:npm install vue-socket.io 2. 在Vue实例中引入VueVueSocketIO,并配置相应的参数。 3. 创建Vuex store,并在配置参数中指定vuex为store的实例。 4. 在Vue实例中使用Vue.use()注册Vue-socket.io,并传入配置参数。 这样,Vue2就可以使用vue-socket.io扩展来实现Socket.io的集成了。可以使用Vue-socket.io提供的方法来监听Socket事件,并实现相应的功能。 总结一下,Vue2使用vue-socket.io扩展可以实现Socket.io的集成,通过注册Vue-socket.io并传入相应的配置参数,可以在Vue实例中实现Socket事件的监听和处理。这样就可以实现实时聊天等功能了。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Vue 使用 Vue-socket.io 实现即时聊天应用vuex管理)](https://blog.csdn.net/weixin_47746452/article/details/121330186)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [vue-socket.io-extended:Vue.js和Vuex的Socket.io绑定(受Vue-Socket.io启发)](https://download.csdn.net/download/weixin_42160425/18170249)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Vue 使用 Vue-socket.io 实现即时聊天应用实战篇 一)](https://blog.csdn.net/weixin_47746452/article/details/121359940)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论 57
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值