vue+websocket realiza mensajería instantánea

Recientemente, Q me pidió que creara una página de mensajería instantánea. En ese momento, una cabeza era tan grande como dos personas, pero después de dos días de investigación, descubrí que en realidad no es muy difícil, así que me gustaría compartir contigo aquí.

1. Introducción

En primer lugar, lo más importante para implementar la mensajería instantánea es websocket, que en realidad es un protocolo de comunicación de red.

En términos generales, durante nuestro proceso de desarrollo, el cliente inicia una solicitud al servidor y luego recibe la respuesta del servidor. Cuando el cliente no inicia una solicitud, el servidor no puede enviar información al cliente. Esto significa que si hay cambios continuos en el lado del servidor, solo podemos usar sondeo (es decir, enviar consultas regularmente) , que es un método que consume muchos recursos, por lo que surgió websocket. Su característica más importante es que el servidor puede enviar solicitudes activamente a los clientes. El identificador de protocolo de websocket para la información push del extremo     
es ws (el cifrado es wss)

                                                   (Imagen de Internet)

2. Cliente (vue usado aquí)

Primero, en cuanto al código clave de websocket, determinamos si websocket es compatible y luego lo monitoreamos, lo que es la inicialización.

Como quería tener una conversación uno a uno, envié un mensaje al principio para almacenar al usuario para poder distribuirlo más tarde.

initWebsocket() {
      let that = this;
      if (window.WebSocket) {
        var ws = new WebSocket("ws://localhost:8001");
        that.ws = ws;
//监听连接成功
        ws.onopen = function(e) {
          console.log("连接服务器成功");
          let msg = {
            type: 1,
            uid: that.uid
          };
          ws.send(JSON.stringify(msg));//向服务器发送信息,这个地方我是先把每一个访问的user都先存储起来方便实现1对1对话
        };
//监听连接失败
        ws.onclose = function(e) {
          console.log("服务器关闭");
        };
//监听报错
        ws.onerror = function() {
          console.log("连接出错");
        };
        // 接收服务器的消息
        ws.onmessage = function(e) {
          let message = JSON.parse(e.data);
          console.log(message);
          that.chatList.push(message);
        };
      }
    },

El siguiente es el código completo de la interfaz vue.

<template>
  <div class="ContactWrap">
    <div class="Contact">
      <div class="ContactSide">
        <div class="ContactSide-tip">联系人</div>
        <div v-for="(item,index) in userList" :key="index">
          <div class="ContactItem" @click="chooseUser(item)" :class="{ChooseItem : item.uid == chooseId}">
            <img class="UserAvator" :src="item.avaUrl" alt="头像" />
            <div class="UserContent">
              <div class="UserMsg">
                <span class="UserName">{
   
   {item.username}}</span>
                <span class="MsgTime">06-22</span>
              </div>
              <div class="UserSnippet">{
   
   {item.content}}</div>
            </div>
          </div>
        </div>
      </div>
      <div class="ContactBox">
        <div class="ContactBox-header">再见</div>
        <div class="MessageBox" ref="MessageBox">
          <div
            v-for="(item,index) in chatList"
            :key="index"
            class="Message"
            :style="item.id == uid?'flex-direction:row-reverse':''"
          >
            <!-- <div class="UserHead"> -->
            <img :src="item.avaUrl" class="UserAvator" />
            <!-- </div> -->
            <div class="UserMsg" :class="item.id == uid?'RightMessage':'LeftMessage'">
              <span :style="item.id == uid?' float: right;':''">{
   
   {item.content}}</span>
            </div>
          </div>
        </div>
        <!-- 输入框 -->
        <div class="InputBox">
          <textarea v-model="msg" class="InputTextarea" rows="3" @keyup.enter="send"></textarea>
          <div class="InputBox-footer">
            <div class="FooterDesc">按Enter键发送</div>
            <button class="sendButton"  @click="send">发送</button>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import util from "@/utils/index.js";

export default {
  name: "contact",
  components: {},
  data() {
    return {
      uid: 0,
      ws: null,
      msg: "", //聊天信息
      chatList: [
        {
          id: 14,
          content: "你说啥",
          avaUrl:
            "https://pic4.zhimg.com/v2-a12b2d609fa2d5d16c10ea069419f3c3_xs.jpg"
        },
        {
          id: 1,
          content: "我说你可真好看",
          avaUrl:
            "https://pic4.zhimg.com/v2-a12b2d609fa2d5d16c10ea069419f3c3_xs.jpg"
        },
        {
          id: 14,
          content: "谢谢夸奖,也就一般般啦",
          avaUrl:
            "https://pic4.zhimg.com/v2-a12b2d609fa2d5d16c10ea069419f3c3_xs.jpg"
        }
      ], //聊天记录
      userList: [
        {
          uid:1,
          username: "再见",
          content: "做好自己",
          time: "6-22",
          avaUrl:
            "https://pic4.zhimg.com/v2-a12b2d609fa2d5d16c10ea069419f3c3_xs.jpg"
        },
        {
             uid:2,
          username: "再见",
          content: "做好自己",
          time: "6-22",
          avaUrl:
            "https://pic4.zhimg.com/v2-a12b2d609fa2d5d16c10ea069419f3c3_xs.jpg"
        },
        {
             uid:3,
          username: "再见",
          content: "做好自己",
          time: "6-22",
          avaUrl:
            "https://pic4.zhimg.com/v2-a12b2d609fa2d5d16c10ea069419f3c3_xs.jpg"
        },
        {
             uid:4,
          username: "再见",
          content: "做好自己",
          time: "6-22",
          avaUrl:
            "https://pic4.zhimg.com/v2-a12b2d609fa2d5d16c10ea069419f3c3_xs.jpg"
        }
      ],
      chooseId:0
    };
  },
  mounted() {
    this.initWebsocket();
    this.uid = util.getUser().id;//可以自己写个假数据
  },
  methods: {
    //创建websocket链接
    initWebsocket() {
      let that = this;
      if (window.WebSocket) {
        var ws = new WebSocket("ws://localhost:8001");
        that.ws = ws;
        ws.onopen = function(e) {
          console.log("连接服务器成功");
          let msg = {
            type: 1,
            uid: that.uid
          };
          ws.send(JSON.stringify(msg));
        };
        ws.onclose = function(e) {
          console.log("服务器关闭");
        };
        ws.onerror = function() {
          console.log("连接出错");
        };
        // 接收服务器的消息
        ws.onmessage = function(e) {
          let message = JSON.parse(e.data);
          console.log(message);
          that.chatList.push(message);
        };
      }
    },
    //发送信息
    send() {
      let toId;
      if (this.uid == 14) {
        toId = 1;
      } else {
        toId = 14;
      }
      let msg = {
        id: this.uid,
        bridge: [this.uid, toId],
        content: this.msg,
        avaUrl:
          "https://pic4.zhimg.com/v2-a12b2d609fa2d5d16c10ea069419f3c3_xs.jpg"
      };
      this.ws.send(JSON.stringify(msg)); //这里是把聊天内容发给服务端
      this.msg = "";
      setTimeout(() => {
        this.scrollBottm();
      }, 200);
    },
    //滚动条滚动到底部
    scrollBottm() {
      let el = this.$refs["MessageBox"];
      el.scrollTop = el.scrollHeight;
    },
    //选择联系人
    chooseUser(user){
        this.chooseId = user.uid;
    }
  }
};
</script>
<style lang="scss" scoped>
.Contact {
  height: 614px;
  width: 1002px;
  background-color: #fff;
  border: 1px solid #ebebeb;
  box-shadow: 0 0 4px 0 rgba(26, 26, 26, 0.1);
  border-radius: 3px;
  margin: 8px auto 0;
  display: flex;
  //联系人
  .ContactSide {
    width: 286px;
    height: 100%;
    border-right: 1px solid #ebebeb;
    .ContactSide-tip {
      height: 40px;
      line-height: 40px;
      font-weight: 600;
      padding: 0 30px;
      border-bottom: 1px solid #ebebeb;
    }
  }
  .ContactItem {
    padding: 12px 20px 12px 29px;
    cursor: pointer;
    display: flex;
    border-bottom: 1px solid #f7f8fa;
    .UserContent {
      flex: 1;
      .UserMsg {
        display: flex;
        align-items: center;
        justify-content: space-between;
        .UserName {
          font-size: 15px;
          columns: #444444;
          font-weight: 600;
        }
        .MsgTime {
          font-size: 12px;
          color: #999999;
          float: right;
        }
      }
    }
  }
  .ChooseItem{
    background: #f5f4f4;
 }
  .UserAvator {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    margin-right: 10px;
  }
  .ContactBox {
    width: 710px;
    &-header {
      font-size: 15px;
      margin: 0 14px;
      height: 20px;
      padding-bottom: 9px;
      padding-top: 21px;
      border-bottom: 1px solid #ebebeb;
      font-weight: 600;
      text-align: center;
    }
    //聊天框
    .MessageBox {
      height: 362px;
      overflow: scroll;
      .Message {
        display: flex;
        margin: 20px;
        .UserMsg {
          max-width: 388px;
          border-radius: 8px;
          padding: 6px 12px;
          font-size: 14px;
          position: relative;
          margin: 0 8px;
          text-align: left;
          white-space: pre-wrap;
          word-break: break-all;
        }
        .LeftMessage {
          background-color: #f6f6f6;
          color: #444;
          &::after {
            content: "";
            position: absolute;
            width: 8px;
            height: 8px;
            left: -4px;
            top: 14px;
            background: #f6f6f6;
            -webkit-transform: rotate(45deg);
            transform: rotate(45deg);
          }
        }
        .RightMessage {
          background-color: #0084ff;
          color: #fff;
          &::after {
            content: "";
            position: absolute;
            width: 8px;
            height: 8px;
            right: -4px;
            top: 14px;
            background: #0084ff;
            -webkit-transform: rotate(45deg);
            transform: rotate(45deg);
          }
        }
      }
    }
    //输入框
    .InputBox {
      padding: 0 10px;
      border-top: 1px solid #ebebeb;
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      height: 170px;
      background: #fff;
      z-index: 10;
      .InputTextarea {
        margin-top: 20px;
        width: 100%;
        border: none;
        font-size: 14px;
        flex: 1;
      }
      &-footer {
        display: flex;
        align-items: center;
        justify-content: flex-end;
        height: 50px;
        .FooterDesc {
          font-size: 14px;
          color: #bfbfbf;
          padding-right: 10px;
        }
        .sendButton {
          color: #fff;
          background-color: #0084ff;
          border-radius: 6px;
          width: 72px;
          height: 32px;
          font-size: 13px;
          line-height: 32px;
          text-align: center;
        }
      }
    }
  }
}

</style>

3. Servidor (servicio temporal de nodo)

Aquí debe instalar un complemento nodejs-websocket, usar el nodo para iniciar un servicio en el puerto 8001 y buscar directamente el nombre del archivo de nodo de la carpeta actual cuando se ejecuta.

var ws = require("nodejs-websocket");
let test = {};
let users = [];
console.log("开始链接");
//如果群聊可以使用广播,这里没用
function boardCast(obj) {
  //群聊
  // server.connections.forEach(function (conn) {
  //   conn.sendText(JSON.stringify(obj));
  // })
}

var server = ws.createServer(function (conn) {
  conn.on("text", function (res) {
    //处理前端发来的请求
    data = JSON.parse(res);
    console.log("来人了");
    console.log(data);
    //把所有uid对应的链接conn存到一个对象里面
    if (data.type == 1) {
      //如果是新用户就走这里添加到user
      if (users.indexOf(data.uid) == -1) {
        users.push(data.uid);
        test[`${data.uid}`] = conn;
      }
    } else {
      console.log('发送')
      //没有type就代表是发送信息
      data.bridge.forEach(item => {
        test[item].sendText(JSON.stringify(data));
    });
    }
  })

  conn.on("close", function (code, reason) {
    console.log("关闭连接")
  });
  conn.on("error", function (code, reason) {
    console.log("异常关闭")
  });
}).listen(8001)
console.log("websocket建立完毕")

Puede abrir dos páginas y establecer ID diferentes. Puede haber muchos problemas al escribir por primera vez. Esto es sólo para darle una idea. Si hay algún error al escribir, por favor deme algún consejo.

Supongo que te gusta

Origin blog.csdn.net/wuguidian1114/article/details/107339141
Recomendado
Clasificación