vue+websocket realizes instant messaging

Recently, I was asked by Q to make an instant messaging page. At that time, one head was as big as two people, but after two days of research, I found that it is actually not very difficult, so I would like to share it with you here.

1. Introduction

First of all, the most important thing to realize instant messaging is websocket. Websocket is actually a network communication protocol.

Generally speaking, during our development process, the client initiates a request to the server and then receives the server's response. When the client does not initiate a request, the server cannot send information to the client. This means that if there are continuous changes on the server side, we can only use polling (that is, sending queries regularly) , which is an extremely resource-consuming method. Therefore, websocket came into being. Its biggest feature is that the server can actively send requests to clients. The protocol identifier of websocket for end-side push information     
is ws (encryption is wss)

                                                   (Picture from the Internet)

2. Client (vue used here)

First, about the key code of websocket, we determine whether websocket is supported, and then monitor it. This is initialization.

Since I wanted to have a one-on-one conversation, I sent a message at the beginning to store the user so that it could be distributed later.

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);
        };
      }
    },

The following is the complete vue interface code

<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. Server (node ​​temporary service)

Here you need to install a plug-in nodejs-websocket, use node to start a service on port 8001, and directly find the node file name of the current folder when running.

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建立完毕")

You can open two pages and set different IDs. There may be many problems when writing for the first time. This is just to provide you with an idea. If there are any mistakes in writing, please give me some advice.

Guess you like

Origin blog.csdn.net/wuguidian1114/article/details/107339141