48
头图

Preface

At the beginning of last month, I received a request to develop a chat communication module and integrate it into multiple entries in the project to achieve record tracking of business data.

After receiving the demand, quite happy, this is my first time out demand communications, and before been B side of the business needs, but is now doing in this direction, feeling B end direction quite interesting, with project management The entire project upstream and downstream, and then used by internal and external personnel, I feel very proud.

Let's follow me to see how to develop a chat communication service! (Mainly from the front-end point of view to talk about how to develop and design)

Technology stack

徽章.png

  • Vue 2.x
  • Websoket
  • Vuex
  • Element
  • vue-at

This project is Vue technology stack ecology. In fact, no matter what language is used, the idea is the key! Know what needs to be done at each step, and then integrate each step of the operation, and finally the service will run.

What is each step function is the 06149f843e796e function in programming. According to this function, what technical points need to be analyzed in detail. In the development process, you can't be technical points of the entire link. If you encounter any difficulties, you can learn temporarily.

Start analyzing requirements

First of all, we have to wait UI designers design draft drawn, then according to UI designer draft design structural analysis of the entire chat communication from view structure should be divided generally include what component , each component in which also includes a small component , In this way, converts the design draft into 06149f843e79ba from the programmer's perspective from component .

Having established which component , the next step is to determine which functions component Now UI interface, they will use third-party software/platform to convert the renderings into web pages, and they can URL . When the cursor is placed on an element on the page, you can get it To the current element's css style , but I suggest not to copy , sometimes it will conflict with the layout code written by yourself, copy .

Effect picture

I will not release the actual renderings here. For confidentiality, I only list the overall structure, and then take everyone to analyze the structure and function, how to code design and component design.

Functional analysis chart

According to the renderings, I should remember this principle when dividing components: High cohesion, low coupling , single component responsibility

We divide the components into:

  • contact component
  • chat component ---- includes history component

The function is determined based on URL webpage provided by the UI designer to see the interactive effect, and communicate with the team leader / product manager, determine the demand, and cut off the unreasonable demand.

After the requirements are determined, it is time to sort out the functions of the component part.

Component composition

Before analyzing the components, we need to know about Vue Component . Vue should be familiar with it. The composition of a component consists of the following:

  1. data component internal state
  2. computed calculate attributes, monitor the data to achieve the corresponding business logic requirements
  3. watch monitor the changes of state
  4. method group general function writing area
  5. props component accepts parent component, and performs constraint types, etc.
  6. lifecycle The life cycle of the component, the corresponding business logic can be executed from the creation to the destruction of the component

Contact component

This component is mainly used when chatting, you can quickly find someone to contact it by grouping, and the function is relatively simple.

Function:

  1. Find a contact
  2. Notify someone of the operation

Functional Analysis

Function 1: Find contacts

Find the entered contact to match with the existing contact json (simple)

Function 2: Notify someone

When the user clicks on a contact, the clicked person is placed in the input box to display @xxx [formatted], and the selected contact information is added to the json object that sends the message.

There are multiple implementation schemes. When the user clicks on a contact, the event will be triggered, and the value will be passed to the parent component [the entry index.vue of the chat component] to receive it, and then the value will be passed to the chat body component, through the In the chat body component, the value is passed through $refs

Only sample code is provided below

Get the selected contact from the contact list

//联系人组件 concat.vue
​
​
getLogname(val){
    this.$emit('toParent',{tag:'add',logname:val})
},

chat box displays the selected contact

Receiving chat inlet assembly sub-contact data transfer to the selected parent component and to chat binding body assembly ref , by refs to pass data to the contact chat display body assembly. [There are many ways to transfer this piece of data, such as Vuex ]

//聊天组件入口 index.vue   它包括 联系人组件  聊天主体组件  历史记录组件
​
//联系人组件
<Concat @toParent='innerHtmlToChat'/>
​
//聊天主体组件    
<ChatRoom @fullScreen="getFullStatus" @closeWindow="close" ref="chatRoom"/>
​
​
    
 // 接受
 innerHtmlToChat(data){
    this.$refs.chatRoom.$refs.inputConents.innerHTML+=`&nbsp;@&nbsp;${data.logname}`  //拼接到聊天输入框里
},     
​

effect display

Select a person from the contact list and send a message

@人 received push message

Chat body component

This component is responsible for more functions, this part I mainly bring you the key functions to analyze again

Key functions;

  1. @ friend function, realize push notification (online notification / offline-online notification)
  2. Chat tool [ supports emoticons supports large file upload]
  3. Send a message [ this piece can be linked to the business, when sending the message, carry some data that meets your project needs]

Functional Analysis

Function 1: @ Realize

vue-at Document: https://github.com/von7750/vue-at

Its function and micro-channel and QQ @ features like chat input box, when you enter @ key to bring up the buddy list, then select a contact to chat.

@ function must include the following 3 key functions;

  • Can pop up contact list
  • You can monitor the input character content to filter and display the corresponding data
  • Delete @ contact
  • .......

At the beginning, I built a @ functional wheel myself, and then I found out that there is a corresponding wheel on the market, and I used a third party directly. vue-at

Let’s follow me next to get a feel for how to implement this wheel. I won’t include the implementation code here.

Let's analyze a wave first:

When you enter @ in the editing area, a pop-up box will pop up

  1. We can mounted monitor the key life cycle code time = 50/229 (Chinese / English), make a deal
  2. Because of our div editable attribute, we get the cursor position of the editable attribute
  3. Then dynamically change the style of the contact list in the pop-up box by the cursor position to top left to display the contact list following the cursor position.
  4. Then select a contact from the list to chat, and contact list pop-up box.

selected contact function is realized above.

Delete the selected contact

Since this piece is an editable attribute, we can get the selected person, but cannot directly determine which person is deleted . At this time, we can only delete the saved by judging whether a contact is contained in innerHTML Contact.

At this time, the business requirements have been basically met.

The third-party plug-ins are good enough, we don't need to reinvent the wheel and waste time, but we must understand the ideas. Below, I will demonstrate how to use the third-party plug-in vue-at achieve the function of @

1. Install the plug-in

npm i vue-at@2.x

2. Import the plug-in component inside the component

import At from "vue-at";

3. Registered plug-in component

 components: {
        At
 },

4. Use

At component must include the editable input content area, so that when you input @ , the contact list box will pop up.

  • members : data source
  • filter-match : Filter data
  • deleteMatch : deleted by 16149f843e82f2
  • insert : Get contacts
<At
    :members="filtercontactListContainer"
    :filter-match="filterMatch"
    :deleteMatch="deleteMatch"
    @insert="getValue"
    >
    <template slot="item" slot-scope="s">
        <div v-text="s.item" style="width:100%"></div>
    </template>
    <div
         class="inputContent"
         contenteditable="true"
         ref="inputConents"
         ></div>
</At>
// 过滤联系人
filterMatch(name, chunk) {
    return name.toLowerCase().indexOf(chunk.toLowerCase()) === 0;
},
// 删除联系人
deleteMatch(name, chunk, suffix) {
    this.contactList = this.contactList.filter(
            item => item.logname != chunk.trim()
        );
  return chunk === name + suffix;
},
// 获取联系人
getValue(val) {
     this.contactList.push({ logname: val });
},

Function 2: Chat Toolbox

In addition to ordinary text chat, the chat software also has some auxiliary services to increase the richness of the chat, such as: emoticons, file upload, screenshot upload... function

Let's first take a look at the popular chat software in the market which chat tools they have.

WeChat chat toolbox

  • emoticon
  • file upload
  • screenshot
  • chat history
  • video chat / voice chat

QQ Chat Toolbox

  • emoticon
  • GIF
  • screenshot
  • file upload
  • Tencent document
  • picture sending
  • ..... Tencent business related functions

Introduced what tools are in the popular chat toolbox on the market, back to the main topic: chat toolbox, in fact, which functions are determined according to the business, and the later toolbox can be continuously expanded. Our toolbox basically meets daily chat needs

  • emoticon
  • File upload supports large files (several Gs are fine)
  • screenshot Ctrl + Alt + A
  • history

Now I will compare several important functions: file upload and screenshot, other functions are very simple.

file upload

The upload component I use is the Element el-upload component. Because my business requires uploading files to support large files, I use the segmented resuming upload method.

ideas

  1. Our upload is also the websoket upload. When you send it for the first time, you must send some basic file information.

    • file name
    • File size
    • sender
    • Some business-related field data
    • time
    • File fragment size
    • Number of file fragments
    • Upload progress indicator
  2. After the basic information of the file is sent for the first time, start to send the fragmented file information. First, the file is fragmented, and then the fragmented file stream is read in sequence, and the file stream is carried when sending. After the file fragmentation cycle ends, an end flag is sent to tell The background sending is finished [ you can discuss the design data format with the backend]

sample code demo

<el-upload
           ref="upload"
           class="upload-demo"
           drag
           :auto-upload="false"
           :file-list="fileList"
           :http-request="httpRequest"
           style="width:200px"
           >
    <i class="el-icon-upload"></i>
    <div class="el-upload__text" trigger>
        <em> 将文件拖到此处然后点击上传文件</em>
    </div>
</el-upload>

Override the Element and use the custom upload method instead.

Start multipart upload

    // 上传文件
    httpRequest(options) {
      let that = this;
​
      //每个文件切片大小
      const bytesPerPiece = 1024 * 2048;
     // 文件必要的信息
      const { name, size } = options.file;
     // 文件分割片数
      const chunkCount = Math.ceil(size / bytesPerPiece);
      
    // 获取到文件后,发送文件的基本信息
      const fileBaseInfo = {
        fileName: name,
        fileSize: size,
        segments: "historymessage",
        loginName: localStorage.getItem("usrname"),
        time: new Date().toLocaleString(),
        chunkSize: bytesPerPiece,
        chunkCount: chunkCount,
        messagetype: "bufferfile",
        process: "begin",
          
          
        ... 一些跟业务挂钩的 字段
​
      };
​
​
      that.$websoketGlobal.ws.send(JSON.stringify(fileBaseInfo));
      
      let start = 0;
​
      // 进行分片
      var blob = options.file.slice(start, start + bytesPerPiece);
      //创建`FileReader`
      var reader = new FileReader();
      //开始读取指定的 Blob中的内容, 一旦完成, result 属性中保存的将是被读取文件的 ArrayBuffer 数据对象.
      reader.readAsArrayBuffer(blob);
      //读取操作完成时自动触发。
      reader.onload = function(e) {
        // 发送文件流
        that.$websoketGlobal.ws.send(reader.result);
        start += bytesPerPiece;
        if (start < size) {
          var blob = options.file.slice(start, start + bytesPerPiece);
          reader.readAsArrayBuffer(blob);
        } else {
          fileBaseInfo.process = "end";
          // 发送上传文件结束 标识
          that.$websoketGlobal.ws.send(JSON.stringify(fileBaseInfo));
        }
        that.uploadStatus = false;
        that.fileList = [];
      };
    },

effect demonstration

Function 3: Screenshot function

In PC , this is a very important business. Through this technology, you can intercept pictures of articles you are interested in from the Internet for your own use and watch, which can help people better understand the use of knowledge.

Since our input content area uses the editable area, you can insert any content here, or you can use the external screenshot function and paste it into the input box area. This unnecessary wheels .

1. Editable area

We div add the attribute contenteditable to control div . The content copied from outside can also be displayed directly, and the effect css Let's take a look at what attributes contenteditable

valuedescribe
inheritThe default value is inherited from the parent element
trueOr an empty string, indicating that the element is editable;
falseIndicates that the element is not editable.
plaintext-onlyPlain Text
caretsymbol
events

Note

<label contenteditable>Example Label</label> not allowed

The correct usage is <label contenteditable="true">Example Label</label> .

Browser support status

uses

<div
     class="inputContent"
     contenteditable="true"
     ref="inputConents">
</div>

effect display

2. Screenshot

Since is editable, you can freely copy from the outside, haha, the interesting thing is coming. It supports screenshots that come with Windows + third-party PC screenshots......

💥Quick operation method:

  • windows built-in screenshot shortcuts

    Print Screen entire screen 06149f843e8a60

    Capture the current active screen Alt+Print Screen

  • QQ screenshot function, support personalized operation screenshot Ctrl + Alt + A
  • WeChat screenshot function, support personalized operation screenshot Alt + A
  • Dedicated screenshot tool....

Stand on the shoulders of giants and take off directly. 😄, but I do think from the user's point of view, this is really a bit bad 😘.

actual effect demonstration

2.1 WeChat screenshot show time

2.2 QQ screenshot

Function 4: Send function

The chat function throughout this project, the project uses a websoket communications service implementation, full-duplex communication, send chats, you need to carry some very business-related data to achieve business tracking analysis. Next, let’s briefly review websoket , and learn it for students who have never used websoket

WebSoket

WebSocket is a protocol for full-duplex communication on a single TCP connection. WebSocket makes the data exchange between the client and the server easier, allowing the server to actively push data to the client. In the WebSocket API, the browser and the server only need to complete a handshake, and a persistent connection can be created directly between the two, and two-way data transmission can be carried out.

WebSoket Features

  • The server can actively push information to the client, and the client can also actively send information to the server, which is a true two-way equal dialogue.
  • Belongs to a kind of server push technology.
  • It has good compatibility with HTTP protocol. The default ports are also 80 and 443, and the HTTP protocol is used in the handshake phase.
  • The data format is relatively lightweight, the performance overhead is small, and the communication is efficient.
  • You can send text or binary data.
  • There is no homology restriction, and the client can communicate with any server.
  • The protocol identifier is ws (if encrypted, it is wss ), and the server website is the URL.

WebSoket Operation API

Create Websoket connection🔗

let socket = new WebSocket("ws://域名/服务路径")

16149f843e8cbd successfully triggered

open() method is triggered when the connection is successful

socket.onopen = function() {
    console.log("websocket连接成功");
};

send message

send() method and pass in a string, ArrayBuffer or Blob .

socket.send("公众号: 前端自学社区")

receives the data returned by the server

message event will be triggered when a new message is received WebSocket

socket.onmessage = function(res) { 
 console.log(res.data)
}

Close WebSoket connection

WebSocket.close() method closes the WebSocke connection or connection attempt (if any). If the connection has been closed, this method does nothing.

socket.onclose = function() {
    // 关闭 websocket
    console.log("连接已关闭...");
    //断线重新连接
    setTimeout(() => {
        that.initWebsoket();
    }, 2000);
};

WebSoket error handling

When websocket due to the occurrence of some error event (such as unable to send some data), a error event will be triggered.

// 监听可能发生的错误
socket.addEventListener('error', function (event) {
  console.log('WebSocket error: ', event);
});

Through the above, we have learned how to use 06149f843e8dc8, the next step is the actual operation, Websoket

The project uses the Vue technology stack, and more writing is biased towards Vue . Since WebSoket runs through the entire project and needs to push @ real time, we put Websoket far as possible in the global entry, and the received information onmessage event is also placed in the entry file, so that the data can be received globally, and the received data is managed and chatted Vuex Data [Historical data push data sending data] .

1. Create a websoket file 06149f843e8e2d for global use

export default {
    ws: {},
    setWs: function(wsUrl) {
        this.ws = wsUrl
    }
}

2. In Vue entry file index.js registered in the global

import Vue from 'vue'
import websoketGlobal from './utils/websoket'

Vue.prototype.$websoketGlobal = websoketGlobal

3. Receive Websoket push messages in App.vue

The design of this piece is very important, it determines the storage and design of the chat data, too detailed code will not put .

Let me talk about the general idea:

  • The transmission format is set, then the received data structure is also set, more in the context of the data structure, the front and back ends need to constrain the field attributes.

    Judging from the display status of the chat page:

    1. Distinguish the field of the data type, so that when the front end receives the push message, it knows how to display it on the page, for example (show image style or text style)
    2. Distinguish the sent message and display the left and right fields. When the front end receives the push message, it will first determine whether it is for itself. If not, it will be displayed on the left.
    3. Distinguish the push field of the system, and display the corresponding style according to this field.
    4. ........... More field attributes need to be determined according to your actual business

    Yes

    Yes

    Judging from the information push status:

    1. @ push global Notification notification and chat internal push design

      • @ push is judged according to the specified field type, and then realizes global push
      • chat content push: Because it is related to a specific chat, it also belongs to historical chat data. In the chat, the content data type of is used to determine how to display
mounted(){
    this.$websoketGlobal.ws.onmessage = res => {
        const result = JSON.parse(res.data);
​
        // 推送数据
​
        //聊天历史数据 新增加发送的数据
​
​
        // 获取聊天历史数据
​
        //聊天历史数据 新增加发送的数据
​
    };
}

4. Use Websoket in the chat component

In the chat component, what is actually used is the sending function and the historical record function, and the other is to decide how to display the data on the page according to the pushed message content field. The following chat style code will not be put, mainly put the sample code for sending messages.

send() {
    let that = this;
​
    // 定义数据结构: 传递什么内容是 前提 前端和后端商量好的  
    const obj = {
        messageId: Number(
            Math.random()
            .toString()
            .substr(3, length) + Date.now()
        ).toString(36),
        //文件类型  
        messagetype: "textmessage",
        //@ 联系热
        call: that.contactList,
        //聊天输入内容  
        inputConent: that.$refs.inputConents.innerHTML ,
        // 当前时间  
        time: currentDate,
​
        ..... 再定义一些符合你业务的字段    
    };
    
    // 发送消息
    that.$websoketGlobal.ws.send(JSON.stringify(obj));
    that.$refs.inputConents.innerHTML = "";
    that.contactList = []
}
},

Each time you enter the chat component, you need to first obtain the history of the chat. The chat entry is determined by your business, and the necessary parameters are passed.

mounted(){
    this.$websoketGlobal.ws.send(
        JSON.stringify({
            id: 1
            messagetype: "historymessage"
        })
    );
}

Function 5: Offline/Online Push

This is equivalent to WeChat / QQ online and online messages received. When user A @ user B ( user B is not online at this time), when user B goes online, it will receive a message. achieve this?

I'll talk about the ideas in general based on the project, and I won't talk about the specific implementation. The implementation is mainly in the back-end. At that time, I also specifically asked the back-end boss for advice.

\
When user A logs in to the system, it will Websoket , and the backend will record the user's identity and status as logged in.

When user A @ becomes user B, the normal logic will push a message to user B, but if user B is not online, wouldn't it be pushed to him?

know whether user B is online?

As mentioned earlier, the login system will establish a connection, and the backend will temporarily store online users. When user A sends a message to user B, the backend sees that there is no user B in the online user list, then he will not Push. When user B goes online, it will be automatically pushed, received by the front-end, and directly reminded the user.

Chat room entrance component

The chat room entrance components include: contact component + chat main component, what it does is actually very simple.

  1. How to open a chat room?
  2. How to pass historical data to the chat room?

How to open a chat room?

The outside may open the chat room through multiple entrances, and control the display of the chat room through a state, the transfer type is Boolean

How to pass historical data to the chat room?

The external passes necessary data to the chat room component, which is then contact component and the chat body component to obtain the data required by each, so that the chat room entrance component has a single responsibility and is easy to manage.

Let's take a look at the entry component of the chat room:

<template>
  <div>
    <transition name="el-fade-in-linear" :duration="4000">
      <div
        class="chat-container"
      >
        <div
          class="left-concat"
        >
            //联系人组件
          <Concat @toParent="innerHtmlToChat" />
        </div>
        <div
          class="right-chatRoom"
        >
            // 聊天室主体组件
          <ChatRoom
            ref="chatRoom"
          />
        </div>
      </div>
    </transition>
  </div>
</template>

The internal communication is mainly managed by Vuex . Since the chat room needs to be awakened globally, the chat entry component can be placed in the global entry file. In this way, no matter how many entries are needed for the project, you only need to pass the wakeup chat entry component. Status and necessary parameters required by the entry component to obtain historical chat data.

<Chat
      // 控制是否显示聊天室
      v-if="$store.state.chatStore.roomStatus"
      //聊天室需要的必要数据
      :orderInfo="$store.state.chatStore"
 />

Thus, when the other module needs to project chat this feature, only line of code can access as an access slot.

<template slot="note" slot-scope="props">
    <i class="el-icon-chat-dot-square"  @click="openChatRoome(props.data.row)"></i>
</template>
openChat(row){
    this.$store.commit("Chat", { status: true, data: row });
},

Summarize

In the development of this chat service, I also encountered many difficulties and pitfalls, but I stepped on them one by one. After developing this chat service, you have a deeper understanding of technology. When you feel that a certain function is difficult and difficult, and you don’t know how to implement it, you first act and reason step by step according to your own ideas. The reasoning process will be When the idea is opened, there will be multiple ways to achieve it.

finally

The chat service has been developed for a month, and it has been a week or so to write an article. It is not easy to write. If you have learned the article, please like it 👍👍👍Follow and support!

Follow the public : 16149f843e93c7 Front-end self-study community , join the self-study exchange group, grow and learn together!


程序员海军
942 声望7k 粉丝