preface

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

After receiving demand, also pretty happy, this is my first time to make the demand of communication class, before has been the B of the business needs of the client, but it is also doing this direction, feel interesting B side direction, manages the whole project upstream and downstream of the project, and then in the service of internal and external personnel to use, feel very proud.

Now follow me to see how to develop a chat communication service! (Mainly from the perspective of front-end development and design)

Technology stack

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

This project is to Vue technology stack ecological development, in fact, no matter what language, thinking is the key! You know what each step needs to do, you integrate each step, and eventually the service runs.

What needs to be done in each step is the function function in programming, according to which technical points need to be refined and analyzed. You won’t be familiar with all the technical points of the link during development, so you’ll have to improvise.

Start analyzing requirements

First, we need to wait for the UI designer’s design draft to be drawn. Then, according to the UI designer’s design draft, we analyze the structure of the overall chat communication, and divide the view structure into roughly which components should be included. What smaller components should be included in each component, so that the design can be translated into components from the programmer’s perspective from large to small.

Now that you’ve identified the components, the next step is to determine what each of the smaller components does. Now UI designers, generally after drawing the interface, will use third-party software/platform to convert the effect map into a web page, and can be accessed directly through the URL, when the cursor is placed on the page of an element, you can get the CSS style of the current element, but I recommend not copy, Sometimes and write their own layout code will conflict, copy as needed.

rendering

I’m not going to show the actual renderings here, but for the sake of confidentiality, I’m just going to lay out the overall structure, and then I’m going to walk you through the structure and the function, how to do the code design and the component design.

Functional diagram

According to the renderings, I want to keep this principle in mind when dividing components: high cohesion, low coupling, single responsibility of components

We divide the components into:

  • Contact component
  • Chat components– including theHistory component

Functions are determined based on the URL page provided by the UI designer to see the interaction effect, and communicate requirements with the team leader/product manager, determine requirements, and cut unreasonable requirements.

Once the requirements are identified, it’s time to tease out the functionality of the component parts.

components

Before we analyze components, we need to take a look at the Vue Component. Those who use Vue should be familiar with it. A Component consists of the following components:

  1. Data Internal status of the component

  2. For computed attributes, you listen for changes in data to meet corresponding business logic requirements

  3. Watch listens for state changes

  4. Method group will write the function area

  5. The props component accepts values passed by the parent, performs constraint types, and so on

  6. Lifecycle is the lifecycle of a component that can execute the corresponding business logic between the creation and destruction of the component

Contact component

This component is mainly used for chatting, you can quickly find someone to contact it through groups, the function is relatively simple.

Function:

  1. Searching for contacts
  2. Someone is notified to operate

Functional analysis

Function 1: Search for contacts

Search for the input contact through the existing contact JSON data for matching. (simple)

Function 2: Notify someone

When the user clicks on a contact, put the person in the input box to display @xxx [formatted], and add the selected contact information to the JSON object that sends the message.

There are several implementations where, when a user clicks on a contact, the event is triggered, carrying a value that is passed to the parent component [index.vue, the entry to the chat component] to receive, and then passed to the chat principal component by passing the value through $refs in the chat principal component.

Only sample code is provided below

Obtain the selected contact from the contact list

// Contact component concate.vue
​
​
getLogname(val){
    this.$emit('toParent', {tag:'add'.logname:val})
},
Copy the code

The chat box displays the selected contact

In the chat entry component receiving child to the parent component to pass selected contact data, and then to the chat main component binding ref, through refs to pass the contact data to the chat main component display. [There are many ways to transfer this data, such as Vuex]

Vue includes a contact component, a chat body component, a history component// Contact component
<Concat @toParent='innerHtmlToChat'/>
​
// Chat body component
<ChatRoom @fullScreen="getFullStatus" @closeWindow="close" ref="chatRoom"/>
​
​
    
 / / to accept
 innerHtmlToChat(data){
    this.$refs.chatRoom.$refs.inputConents.innerHTML+=`&nbsp; @&nbsp;${data.logname}`  // Concatenate to the chat input box
},     
​
Copy the code

Results show

Select the person from the contact list and send the message

The @person receives the push message

Chat body component

This component is responsible for many functions, this part I mainly take you to analyze the key functions

Key functions;

  1. @Friends function, realize push notification (online notification/offline – online notification)
  2. Chat tools [Support expression Large file upload is supported ]
  3. Send a message [This can then be tied to the business, sending messages with data that fits your project's needs]

Functional analysis

Function 1: @ implementation

Vue-at documentation: github.com/von7750/vue…

Its function is the same as wechat and Q@. In the chat input box, when you enter the @ key, a list of friends will pop up, and then select a contact to chat.

The @ function must include the following 3 key functions;

  • The contact list can be displayed
  • Can monitor the input character content for filtering display corresponding data
  • Delete @Contacts
  • .

At first, I made a @function wheel and played with it. Later, I found that there was a corresponding wheel on the market and used a third party directly. It was a good VUE-AT.

Now, let me give you an idea of how to implement this wheel, without leaving the implementation code here.

Let’s start with a wave:

When you enter @ in the edit area, a pop-up box pops up

  1. We can do it atmountedListen for keystrokes during the life cyclecode= 50/229 (Chinese/English)
  2. Because we used it hereDiv editable propertyGets the cursor position of the editable property
  3. Then change the pop-up contact list style dynamically by cursor positiontop leftTo display the contact list following the cursor position.
  4. Then select the contact from the list to chat with, and selectThe contact list popsHidden away.

The basic contact selection function is implemented above.

Delete the selected contact

Because this is an editable property, we can retrieve the selected person, but we can’t directly determine which person to delete. Instead, we can delete the saved contact by checking whether the contact is included in the innerHTML.

At this point, the business requirements are basically fulfilled.

Third party plugins are good enough, we don’t need to reinvent the wheel and waste time, but the idea must be understood. Next, I will demonstrate how to use the third-party plug-in VUe-at to achieve @ function

1. Install the plug-in

npm i vue-at@2.x
Copy the code

2. Import plug-in components

import At from "vue-at";
Copy the code

3. Register plug-in components

 components: {
        At
 },
Copy the code

4. Use it on the page

The At component must include an editable input area so that a contact list box pops up when @ is typed.

  • members: the data source
  • filter-match: Filter data
  • deleteMatch : Deleted Contacts
  • insert: Obtain 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>
Copy the code
// Filter contacts
filterMatch(name, chunk) {
    return name.toLowerCase().indexOf(chunk.toLowerCase()) === 0;
},
// Delete contacts
deleteMatch(name, chunk, suffix) {
    this.contactList = this.contactList.filter(
            item= >item.logname ! = chunk.trim() );return chunk === name + suffix;
},
// Get the contact
getValue(val) {
     this.contactList.push({ logname: val });
},
Copy the code

Function 2: Chat toolbox

Chat software in addition to ordinary text chat, there are some auxiliary services to increase the richness of chat, such as: expression, file upload, screenshot upload…. function

Let’s take a look at the popular market chat software they have what chat tools.

Wechat chat toolbox

  • expression
  • File upload
  • screenshots
  • Chat record
  • Video chat/voice chat

QQChat kit

  • expression
  • GIF dynamic figure
  • screenshots
  • File upload
  • Tencent document
  • Send a picture
  • . Tencent business related functions

Introduce the popular chat toolbox in the market which tools, back to the topic: what are the functions of our chat toolbox, in fact, which functions according to the business, the later toolbox can be continuously expanded. Our toolbox is basically for everyday chat

  • expression
  • File uploadSupport large files (several gigabytes can be used)
  • screenshots Ctrl + Alt + A
  • The historical record

Here’s a look at some of the most important features: file uploads and screenshots, but everything else is pretty simple.

File upload

I use Element el-upload as the upload component. Since my business requires large files to be uploaded, I use fragment continuation to achieve this.

The idea of fragment continuation

  1. We upload also use websoket upload, the first time to send, must send some necessary file basic information

    • The file name
    • The file size
    • The sender
    • Some business related field data
    • time
    • File fragment size
    • Number of file fragments
    • Upload Progress identifier
  2. After the basic information of the file is sent for the first time, it starts to send the information of the shard file, first fragments the file, then reads the slice file stream in turn, and sends the file stream with it. When the file shard cycle is over, it sends an end flag to tell the background that it is finished sending.

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>Drag the file here and click Upload File</em>
    </div>
</el-upload>
Copy the code

Override Element’s default upload mode in favor of a custom upload mode.

Start fragment upload

    // Upload the file
    httpRequest(options) {
      let that = this;
​
      // Size of each file slice
      const bytesPerPiece = 1024 * 2048;
     // File the necessary information
      const { name, size } = options.file;
     // Number of split files
      const chunkCount = Math.ceil(size / bytesPerPiece);
      
    // After obtaining the file, send the basic information about the file
      const fileBaseInfo = {
        fileName: name,
        fileSize: size,
        segments: "historymessage".loginName: localStorage.getItem("usrname"),
        time: new Date().toLocaleString(),
        chunkSize: bytesPerPiece,
        chunkCount: chunkCount,
        messagetype: "bufferfile".process: "begin". Some business-linked fields}; that.$websoketGlobal.ws.send(JSON.stringify(fileBaseInfo));
      
      let start = 0;
​
      // Sharding
      var blob = options.file.slice(start, start + bytesPerPiece);
      / / create a ` FileReader `
      var reader = new FileReader();
      // Start reading the contents of the specified Blob. Once this is done, the result property holds the ArrayBuffer data object of the file being read.
      reader.readAsArrayBuffer(blob);
      // Automatically triggered when the read operation is complete.
      reader.onload = function(e) {
        // Send a file stream
        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";
          // Send the end of uploading file
          that.$websoketGlobal.ws.send(JSON.stringify(fileBaseInfo));
        }
        that.uploadStatus = false;
        that.fileList = [];
      };
    },
Copy the code

Results demonstrate

Function 3: Screen capture function

In PC, this is a very important business, through this technology can be cut from the Internet interested articles and pictures for their own use to watch, can help people better understand the use of knowledge.

Since our input content area uses an editable area, we can insert any content here, and we can also use the external screenshot function to paste into the input box area, there is no need to make wheels in this area.

1. Editable area

Adding this property to divs gives contenteditable control over what content can be entered in div, as well as the ability to display externally copied content and CSS effects. Let’s start with the properties of contenteditable.

value describe
inherit The default values inherit from the parent element
true Or an empty string indicating that the element is editable;
false Indicates that the element is not editable.
plaintext-only Plain text
caret symbol
events

Pay attention to

Do not abbreviate

The correct usage is .

Browser Support

use

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

Results show

2. Screenshots

Because of the use is editable, so you can freely copy from the external, ha ha, interesting to support Windows own screenshots + PC third-party screenshots……

💥 Quick operation method:

  • Windows built-in screenshot shortcut keys

    Capture the entire Screen

    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

  • Specialized screenshot tool….

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

Practical effect demonstration

2.1 wechat Screenshot show time

2.2 QQscreenshots

Function 4: Send function

This function runs through this chat project, which adopts the communication service realized by Websoket, full duplex communication. When sending chat content, it needs to carry some business-related data to realize business tracking analysis. Next, to a brief review of Websoket, have not used websoket students also study.

WebSoket

WebSocket is a protocol for full duplex communication over a single TCP connection. WebSocket makes it easier to exchange data between the client and the server, allowing the server to actively push data to the client. In the WebSocket API, the browser and server only need to complete a handshake to create a persistent connection and two-way data transfer.

WebSoketThe characteristics of

  • The server can actively push information to the client, and the client can also actively send information to the server, which is a real two-way equal dialogue.
  • Belongs to a server push technology.
  • It has good compatibility with HTTP protocol. The default ports are also 80 and 443, and the handshake phase uses HTTP.
  • The data format is relatively light, with low performance overhead and high communication efficiency.
  • You can send text or binary data.
  • There are no same-origin restrictions, and clients can communicate with any server.
  • The protocol identifier isws(If encrypted, otherwisewss), the server URL is the URL.

WebSoketoperationAPI

Create a Websoket connection 🔗

let socket = new WebSocket("Ws :// domain name/service path")
Copy the code

The Websoket connection triggered successfully

The open() method fires when the connection is successful

socket.onopen = function() {
    console.log("Websocket connection successful");
};
Copy the code

Send a message

The send() method passes in a string, ArrayBuffer, or Blob.

socket.send("Public Account: Front-end Self-learning Community")
Copy the code

Receives data returned by the server

The Message event is raised when the WebSocket receives a new message.

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

Close the WebSoket connection

The websocket.close () method closes the WebSocke connection or connection attempt (if any). This method does nothing if the connection is already closed.

socket.onclose = function() {
    / / close the websocket
    console.log("Connection closed...");
    // Disconnect and reconnect
    setTimeout(() = > {
        that.initWebsoket();
    }, 2000);
};
Copy the code

WebSoket error handling

An error event is raised when a Websocket connection is closed due to some error event (such as an inability to send some data).

// Listen for possible errors
socket.addEventListener('error'.function (event) {
  console.log('WebSocket error: ', event);
});
Copy the code

Now that we know how to use Websoket, let’s go!

The project adopts Vue technology stack, which is more written in Vue. Since WebSoket runs through the project and needs to push @ in real time, we put WebSoket in the global portal as much as possible, and the onMessage event that receives the message is also in the entry file, so that the data can be received globally. The received data uses Vuex to manage chat data [historical data push data send data].

1. Create a new onewebsoketFile for global use

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

In 2.VueEntrance to the fileindex.jsChina National Bureau registration

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

Vue.prototype.$websoketGlobal = websoketGlobal
Copy the code

3. Receive Websoket push messages in app. vue

This part of the design is very critical, determines the chat data storage and design, too many details of the code will not put.

Let me give you a general idea:

  • Transmission format is set, then the received data structure is also set, more is in the data structure up and down, the back end needs to constrain the field attributes.

    According to the status of the chat page:

    1. Data type specificfieldSo that when the front end receives the pushed message, it knows how to display it in the page, for example (should display image style or text style).
    2. Distinguish between the fields displayed on the left and right side of the sent message. When the front end receives the pushed message, it will first judge whether it is itself, and display the left style if it is not
    3. Distinguish the system push field and display the corresponding style according to this field.
    4. . More field attributes will depend on your actual business

    is

    is

    From the status of information push:

    1. @Push Global Notification Notification and chat internal push design

      • @Push is judged by the specified field type, and then global push is implemented
      • Chat content push: Because it is related to a specific chat, it also belongs to historical chat data, which is based on the data in the chatContent data typeTo determine how to display
mounted(){
    this.$websoketGlobal.ws.onmessage = res= > {
        const result = JSON.parse(res.data);
​
        // Push data
​
        // Chat history Data Added data to be sent
​
​
        // Get chat history data
​
        // Chat history Data Added data to be sent
​
    };
}
Copy the code

4. Use Websoket in the chat component

In the chat component, in fact, the function of sending and obtaining history is used, and the content field of the message is pushed to determine how to display the data in the page. Instead of the style code for the chat, we’ll focus on the sample code for sending messages.

send() {
    let that = this;
​
    // Define the data structure: what is passed as agreed between the front end and the back end
    const obj = {
        messageId: Number(
            Math.random()
            .toString()
            .substr(3, length) + Date.now()
        ).toString(36),
        // File type
        messagetype: "textmessage"./ / @ contact heat
        call: that.contactList,
        // Chat input content
        inputConent: that.$refs.inputConents.innerHTML ,
        // The current time
        time: currentDate, ..... Define some more fields that fit your business};// Send a message
    that.$websoketGlobal.ws.send(JSON.stringify(obj));
    that.$refs.inputConents.innerHTML = "";
    that.contactList = []
}
},
Copy the code

Each time you enter the chat component, you need to first get the history of the chat. The chat entrance depends on your business and must be passed parameters.

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

Feature 5: Offline/online push

This is equivalent to wechat/QQ online and online received messages. When user A @ user B (when user B is not online), user B receives A message when user B goes online. How does this work?

I will combine the project to roughly say about the idea, the specific implementation will not say, the implementation of the main back-end. At that time, the back end of the big guy also deliberately consulted.

When user A logs in to the system, A connection will be established with Websoket, and the back end will record the identity of the user, and the status is login.

When user A @ user B, the normal logic would push A message to user B. If user B is not online, would it not push to user B?

How do I know if user B is online?

As mentioned above, A connection will be established after logging in to the system, and the backend will temporarily store online users. When user A sends A message to user B, the backend will see that there is no user B in the list of online users, so he will not push it. When user B goes online, it will automatically push, and the front end will receive it and directly remind the user.

Chat room entry component

Chat room entry components include: contact component + chat body component, it does things actually very simple.

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

How to open a chat room?

The external may open the chat room through multiple entries and control the display of the chat room through a state of type Boolean

How do I pass historical data to a chat room?

External through to the chat room component transfer necessary data, these necessary data and then in the contact component and chat main component internal consumption, to obtain the data they need, so that the chat room entry component of a single responsibility, good management.

Let’s 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"
        >// Contact component<Concat @toParent="innerHtmlToChat" />
        </div>
        <div
          class="right-chatRoom"
        >// The chat room body component<ChatRoom
            ref="chatRoom"
          />
        </div>
      </div>
    </transition>
  </div>
</template>
Copy the code

Internal communication is mainly managed by Vuex, due to the chat room in the global need to wake up, you can put chat entrance components in global entry documents, in this way, no matter how much is needed for the project entry, only need to pass wake up chat entrance component of the state and the parameter of the entrance components require necessary to obtain chat history data.

<Chat// Controls whether chat rooms are displayedv-if="$store.state.chatStore.roomStatus"// The chat room needs the necessary data:orderInfo="$store.state.chatStore"
 />
Copy the code

This way, when other modules of the project need the chat room function, only one line of code is needed to access it, as a slot access.

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

conclusion

In the development of this chat service also encountered a lot of difficulties and pits, but a step over, the more back to do the thinking more open. After developing this chat service, I have a deeper understanding of the technology. When you feel that a function is difficult and do not know how to achieve it, you take action first and reason step by step according to your own ideas. The process of reasoning will be open and there will be a variety of ways to achieve it.

The last

Chat service development for a month, write an article to write a week or so, writing is not easy, if the article learned, click a like 👍👍👍 concern, support!