美文网首页
基于vue2.x实现的即时通讯程序仿微信聊天6对接好友列表以及记

基于vue2.x实现的即时通讯程序仿微信聊天6对接好友列表以及记

作者: 风中凌乱的男子 | 来源:发表于2022-11-05 19:26 被阅读0次
    const api = {
      GetMyFriends:'/api/myfriends'
    }
    
    • 2、在src/api/user.js中新建api接口方法
    // 获取我的好友列表
    export function getMyFriends(data) {
      return request({
        url: api.GetMyFriends,
        method: 'post',
        data,
      })
    }
    
    • 3、复制你新建的api方法名到需要引入到页面,也就是【通讯录页面】,通过import导入
    import { getMyFriends } from '@/api/user'
    
    • 4、然后在methods方法函数内,新建获取好友列表的自定义方法,在该方法内调用getMyFriends接口方法
    methods:{
       async getMyFriendsFun() {
          try {
            const res = await getMyFriends()
            console.log(res)
            this.list = res.data //注意:【list】要在data内预定义成空数组,即 list:[]
          } catch (error) {
            console.log(error)
          }
        }
    }
    
    • 注意:【list】要在data内预定义成空数组,即 list:[ ]

    • 5、然后在mounted生命周期函数内调用getMyFriendsFun方法
      mounted () {
        this.getMyFriendsFun();
      }
    
    • 然后就获取到数据了~
    image.png
    • 6、最后for循环这个list数组,进行数据绑定就搞定了这个通讯录好友列表了
    image.png

    const api = {
      GetChatMsg:'/api/query/msg'
    }
    
    • 3、打开src/api/user.js文件,新建api接口方法,注意看清楚接口的请求方式,根据接口文档来看
    // 获取好友列表记录
    export function getChatMsg(params) {
      return request({
        url: api.GetChatMsg,
        method: 'get',
        params,
      })
    }
    
    • 4、然后复制接口方法名到需要引入的页面,即chatDetail页面
    import { getChatMsg } from '@/api/user';
    
    • 5、因为这个聊天详情页面是动态路由匹配的,所以我们在上个页面跳转的时候,要传入真实的id参数
    • 6、打开通讯录页面,找到跳转方法toDetailPage,修改传入的id参数
    methods:{
      toDetailPage(item) {
          this.$router.push('/chatDetail/'+item._id)
      },
    }
    
    • 7、然后在methods方法函数内新建获取好友聊天记录的自定义方法
    methods:{
      async getChatMsgFun() {
          try {
            const res = await getChatMsg({ toId: this.$route.params.id })
            console.log(res);
            this.list = res.data; //注意:【list】要在data内预定义成空数组,即 list:[]
          } catch (error) {
            console.log(err)
          }
        }
    }
    
    • 注意:【list】要在data内预定义成空数组,即 list:[]

    • 8、然后在mounted生命周期内调用getChatMsgFun方法
    mounted () {
        this.getChatMsgFun();
    },
    
    • 然后就获取到数据了~
    image.png
    • 9、下面就是要渲染该数据了,但是因为我们中间的区域是引入的外部组件,所以要先把得到的list数组数据,通过父子组件传值的形式,传递过去,子组件使用props来接收传递过去的数组
    • 在子组件身上v-bind动态绑定传值
    image.png
    • 子组件内部使用props接收传递过来的list
    image.png
    • 子组件data中的list没用了,就可以删除掉了!!!

    image.png
    • 10、下面就是要处理下渲染出来的数据,根据实际情况渲染,目前是这个样子的
    image.png
    • 先渲染右侧数据,【我是你师傅】和【悟空好绕啊】这两条消息我们现在登陆的这个唐僧的账号发出的,所以,根据接口返回数据和接口返回字段对比
    image.png
    image.png
    • 如果将我们当前登录账户人发送的消息渲染在右侧呢?
    • 其他send_id是发送者的id,那谁是发送者呢?当然是当前登录的账户人是发送者,那么将此send_id当前登录账户人的个人id进行对比,如果相等就是渲染在右侧的!
    • send_id有了,现在的问题是如何获取到当前登录账户人的id呢?
    • 这就需要修改下获取个人信息接口存储的数据
    • 打开src/store/modules/app.js
    image.png
    getUserInfo({ commit, state }) {
        return new Promise((resolve, reject) => {
          getUserInfo().then(response => {
            const { data } = response
            if (!data) {
              return reject('Verification failed, please Login again.')
            }
            const { username } = data
            commit('SET_USER_NAME', data)
            resolve(data)
          }).catch(error => {
            reject(error)
          })
        })
      },
    
    • 这样,我们vuex中state里的userName存储的就是当前登录账户人的个人信息了,就是下图,id就在里面

    image.png
    • 11、我们可以通过vuex的mapGetters来拿到上图中的对象数据
    • 先在chatDetail页面引入mapGetters
    import { mapGetters } from 'vuex';
    
    • 在通过计算属性拿到userName对象
    computed: {
        ...mapGetters(['userName'])
    },
    
    • 12、下面就可以渲染右侧的聊天记录数据了
    image.png
    • 那么右侧的数据就渲染好了,在渲染下头像send_avatar就搞定右侧了
    image.png
    13、下面来分析下如何渲染左侧数据
    • 首先 我们的地址栏参数,那个id就是对方的id,也就是和谁聊天的那个人的id,那么针对左侧的聊天记录来说,左侧的消息是谁发的呢?是对方发的,那么对方就是发送方,那左侧聊天信息的send_id就是发送方的id,那么既然地址栏参数,那个id就是对方的id,那地址栏的id==左侧聊天信息的send_id是不是就可以渲染左侧数据了呢
    image.png
    • 搞定!!!

    <template>
      <div class="chatList">
        <ul>
          <template v-for="(item,index) in list" >
          <!-- 左侧 -->
          <li v-if="$route.params.id==item.send_id" :key="index">
            <div class="left">
              <img :src="item.send_avatar" alt="">
            </div>
            <div class="right">
              <p class="time">{{item.date}}</p>
              <p class="content">
                {{item.message}}
              </p>
            </div>
          </li>
          <!-- 右侧 -->
          <li class="r" v-if="item.send_id==userName._id" :key="index">
            <div class="right">
              <p class="time">{{item.date}}</p>
              <p class="content">
                {{item.message}}
              </p>
            </div>
            <div class="left">
              <img :src="item.send_avatar" alt="">
            </div>
          </li>
        </template>
        </ul>
      </div>
    </template>
    
    <script>
    import { mapGetters } from 'vuex';
    export default {
      props:['list'],
      data() {
        return {
          
        }
      },
      computed: {
        ...mapGetters(['userName'])
      },
    }
    </script>
    
    <style lang="scss" scoped>
    .chatList{
      ul{
        padding: 20px;
        box-sizing: border-box;
        li{
          display: flex;
          margin-bottom: 40px;
          .left{
            width: 92px;
            height: 92px;
            margin-right: 16px;
            img{
              width: 100%;
              height: 100%;
              border-radius: 50%;
            }
          }
          .right{
            flex: 1;
            p{
              margin: 0;
            }
            p.time{
              font-size: 24px;
              margin-bottom: 8px;
            }
            p.content{
              font-size: 28px;
              background: #00ccb8;
              padding: 12px;
              border-radius: 12px;
              color:#fff;
              display: inline-block;
            }
          }
        }
        li.r{
          p.time{
            text-align: right;
          }
          p.content{
            background: #f37d7d;
            text-align: left;
          }
          .left{
            margin-left: 16px;
            margin-right: 0;
          }
          .right{
            text-align: right;
          }
        }
      }
    }
    </style>
    
    image.png
    • 还有一步就是,顶部的对方的名字 需要渲染出来,无非就是在上一级页面跳转的时候多传一个参数,然后这个页面接收渲染一下即可,同学们自己来试试呢??


    • 浅谈socketIO双向通信数据交换技术

    • 何为socketIO?

    • Socket.IO是一个库,基于 Node.js 的实时应用程序框架。可以在浏览器和服务器之间实现实时,双向和基于事件的通信。它适用于每个平台、浏览器或设备,同样注重可靠性和速度。
    • 起源:

    • WebSocket 的产生源于 Web 开发中日益增长的实时通信需求,对比基于 http 的轮询方式,它大大节省了网络带宽,同时也降低了服务器的性能消耗。
    • WebSocket 协议在2008年诞生,2011年成为国际标准。虽然主流浏览器都已经支持,但仍然可能有不兼容的情况,为了兼容所有浏览器,就诞生SocketIO。
    • SocketIO将WebSocket、AJAX和其它的通信方式全部封装成了统一的通信接口,也就是说,我们在使用SocketIO时,不用担心兼容问题,底层会自动选用最佳的通信方式。
    • Socket.io有什么特点?

    • 易用性:Socket.io封装了服务端和客户端,使用起来非常简单方便。
    • 跨平台:Socket.io是跨平台的,可以实现多平台的即时通讯,Socket.io支持跨平台,这就意味着你有了更多的选择,可以在自己喜欢的平台下开发实时应用。由于 iOS 端进行 socket 编程主要使用 GCDAsyncSocket 框架,但要实现 Android、iOS、web 多平台的通讯,还是选择统一的框架或协议比较好。
    • 自适应:Socket.io 实现了实时双向的基于事件的通讯机制,是基于 webSocket 的封装,但它不仅仅包括 webSocket,还对轮询(Polling)机制以及其它的实时通信方式封装成了通用的接口,并且在服务端实现了这些实时机制的相应代码,它会自动根据浏览器从WebSocket、AJAX长轮询、Iframe流等等各种方式中选择最佳的方式来实现网络实时应用,非常方便和人性化,而且支持的浏览器最低达IE5.5。
    • Socket.io 常用内置事件、方法

    • connect 连接socket服务方法(客户端可使用)
    • connection 监听客户端连接的事件(服务端使用on监听使用)
    • disconnect 监听客户端断开链接的事件 (服务端使用on监听使用)
    • emit 注册自定义事件方法(客户端、服务端均可使用)
    • on 监听自定义事件方法(客户端、服务端均可使用)

    • Scoket.io的实际应用

    • (注意:前后端都需要使用Socket.io才能通信)
    • 服务端,基于node.js搭建服务端,需要安装socket.io插件

    cnpm install socket.io --save
    

    ---- 代码演示过程

    var express = require('express');
    var router = express.Router();
    /**
     * socket.io
     */
    var app = express();
    var server = require('http').Server(app);
    var io = require('socket.io')(server, {
      transports: ['websocket']
    });
    server.listen(3001);
    /**
     * 监听客户端连接
     */
    io.on('connection', function (socket) {
      console.log("客户端连接成功~");
    });
    
    
    module.exports = router;
    
    
    • app.js引入这个文件,如果引入过了 就不需要再重复引入了
    require("./routes/index")
    
    • 后端socket服务搭建完成,socket服务地址是: ws://localhost:3001

    • 客户端,基于vue.js搭建客户端,需要安装socket.io-client插件和vue-socket.io-extended插件

    cnpm  install socket.io-client vue-socket.io-extended --save
    
    • main.js内引入自定义js文件socketIo.js
    // 引入socket
    import './utils/socketIo'
    
    • socketIo.js

    import Vue from 'vue'
    import VueSocketIOExt from 'vue-socket.io-extended';
    import io from 'socket.io-client';
    // 链接websocket
    const socket = io.connect('ws://localhost:3001', {
        transports: ['websocket'],
    })
    Vue.use(VueSocketIOExt, socket);
    
    image.png
    image.png
    • 上图就是前后端建立了一个socket通讯服务
    • 下面演示如果进行事件的注册,事件的监听

    • 比如在前端链接上socket服务后,就立马推送一条消息给服务端,然后,服务端推送给所有客户端,说【xxx上线了,并说:xxxxx】

    比如在chatDetail页面mounted生命周期内直接执行事件注册方法

    mounted () {
          this.$socket.client.emit('sayHello', {
          name: this.$route.query.name == '孙悟空' ? '唐僧' : '孙悟空',
          message: this.$route.query.name == '孙悟空' ? '为师想留在女儿国~' : '俺老孙来也~'
        })
      },
    
    image.png
    • 服务端需要监听一下这个sayHello事件,并广播给除自己外的所有人

    /**
     * 监听客户端连接
     */
    io.on('connection', function (socket) {
      console.log("客户端连接成功~");
      //广播给除发送者外的所有人
      socket.on("sayHello",data=>{
        socket.broadcast.emit("broMsg",data)
      })
    });
    
    image.png
    • vue前端监听服务端推送的信息
    sockets:{
        broMsg: function (data) {
          this.info = data
        }
    },
    
    • 然后简单处理下前端页面
     <div class="test" v-if="info" style="font-size: 12px;text-align: center;">
            <span>{{info.name}}</span>上线了,并说了一句<span>{{info.message}}</span>
    </div>
    
    • 最终效果

    image.png
    • 这还是只是广播形式的双向通信,如果要实现一对一的的实时通讯,服务端在推送的时候就要使用

    • to(toId)这个方法了,toId就是对方的唯一标识,只有这样才能推给对方,广播是推给除了自己外的所有人

    • 下面来实现一对一通讯

    • 1、就在chatDetail页面,点击发送按钮 ,发送消息
    image.png
    methods:{
       sendMsg() {
          if (this.value.trim() == '') {
            this.$toast('请输入聊天内容')
            return
          }
          let obj = {
            come_id: this.$route.params.id, //接收方的id
            send_id: this.userName._id, //发送方的id
            type: 1, //消息类型 1 文字消息
            send_avatar: this.userName.avatar, //发送者的头像
            message: this.value, //发送的文字内容
            send_name: this.userName.nickname, //发送者的昵称
            date:new Date(),//发送的当前时间
          }
          this.$socket.client.emit('sayOne', obj)
        }
    }
    
    • 前端部分暂时告一段落,下面是【服务端】监听sayOne事件

    image.png
    • 服务端监听到前端发来的数据,利用to(toId)方法发送给对方
    • 在开发服务端前,前端还是要注册触发一个login事件,传给后端来做【socket用户组】
    • 在刚进入这个页面的时候,也就是在mounted生命周期内,注册触发login事件
    this.$socket.client.emit('login', this.userName._id)
    
    • 服务端来监听login事件,作用域外部定义一个空对象hashName,用来存储【socket用户组】
    socket.on("login", data => {
        // console.log(data);
        var _id = data;
        hashName[_id] = socket.id;
    })
    
    • 服务端监听sayOne事件
     /** 监听sayOne事件 */
      socket.on('sayOne', data => {
        // console.log(data);
        var toName = data.come_id;
        // console.log(hashName);
        // console.log(hashName[toName]);
        if (hashName[toName]) {
          socket.to(hashName[toName]).emit('message', data)
        }
      })
    
    • 下面回到前端,在sockets方法函数里,监听服务端注册message事件,得到服务端推过来的消息
     sockets: {
        message:function(data){
          // console.log(data);
          this.$refs.childCom.list.push(data)
        },
      },
    
    • 如下图
    image.png
    • 最后一步,在push下list数组,渲染右侧数据

    image.png
    sendMsg() {
          if (this.value.trim() == '') {
            this.$toast('请输入聊天内容')
            return
          }
          let obj = {
            come_id: this.$route.params.id, //接收方的id
            send_id: this.userName._id, //发送方的id
            type: 1, //消息类型 1 文字消息
            send_avatar: this.userName.avatar, //发送者的头像
            message: this.value, //发送的文字内容
            send_name: this.userName.nickname, //发送者的昵称
            date:new Date(),//发送的当前时间
          }
          this.$socket.client.emit('sayOne', obj)
          this.$refs.childCom.list.push(obj)
    
        }
    
    • 如下图,已经实现了一对一通讯
    image.png
    • 在最后一步,格式化下时间即可
    image.png
    {{$formatDate(item.date,'yyyy-MM-dd hh:mm:ss')}}
    
    image.png

    相关文章

      网友评论

          本文标题:基于vue2.x实现的即时通讯程序仿微信聊天6对接好友列表以及记

          本文链接:https://www.haomeiwen.com/subject/cmomtdtx.html