Vue聊天对话框详解

发布时间:2023-05-20

一、基本结构

<template>
  <div class="chat-room">
    <div class="chat-history">
      <div v-for="message in messages" class="chat-message" :class="{ 'my-message': message.isMyMessage }">
        <div class="message-details">
          <p class="username">{{ message.username }}</p>
          <p class="time">{{ message.time }}</p>
        </div>
        <p class="message-body">{{ message.body }}</p>
      </div>
    </div>
    <div class="chat-input">
      <input type="text" placeholder="Type a message" v-model="newMessage" @keyup.enter="sendMessage">
      <button class="send-message-btn" @click="sendMessage">发送</button>
    </div>
  </div>
</template>

以上是一个简单的聊天对话框组件的基本结构,其中包括两部分:chat-historychat-inputchat-history 是展示聊天记录的部分,chat-input 是用户输入聊天内容的部分。 在组件内部,我们需要定义一个序列,用来保存聊天记录信息,即 messagesmessages 数组中的每一项应该包含三个属性:usernametimebody。当用户发送聊天信息时,我们应该在 input 框中以 v-model 的形式绑定到一个叫做 newMessage 的数据上,同时通过 keyup.enter 事件监听用户回车输入,并执行 sendMessage 方法。 以下是组件内 data 以及 sendMessage 方法的基本结构:

<script>
export default {
  data() {
    return {
      messages: [],   // 聊天记录序列
      newMessage: ''  // 用户输入的聊天信息
    }
  },
  methods: {
     sendMessage() {
       // 创建消息对象
       let newMsg = {
         username: '某某用户',
         time: new Date().toLocaleString(), // 获取当前时间并格式化
         body: this.newMessage,
         isMyMessage: true // 标记为本人发出的信息
       };
       // 将消息添加到消息序列中去
       this.messages.push(newMsg);
       // 将input框的值清空
       this.newMessage = '';
       // 滚动到最底部
       this.$nextTick(() => {
         this.$refs.chatHistory.scrollTop = parseInt(window.getComputedStyle(this.$refs.chatHistory).height);
       });
     }  
   }
};
</script>

sendMessage 方法中,我们在将用户发送的聊天记录 pushmessages 序列中之后,还需要执行输入框清空、机器回复、窗口滑动到最下方的任务。

二、样式美化

.chat-room {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  height: 400px;
  width: 400px;
  border: 5px solid #C1C1C1;
  border-radius: 5px;
  box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.2);
  overflow: hidden;
}
.chat-history {
  flex: 1;
  padding: 20px 20px 0;
  margin-bottom: 10px;
  overflow-y: scroll;
  word-break: break-all; /* 长文本断行 */
}
.chat-message {
  display: flex;
}
.message-details {
  margin-right: 10px;
  font-size: 14px;
  color: #999;
}
.my-message .message-details {
  margin-left: 10px;
  margin-right: 0;
  text-align: right;
}
.message-body {
  background-color: #EEE;
  padding: 10px 15px;
  border-radius: 10px;
  max-width: 75%;
}
.my-message .message-body {
  background-color: #4CAF50;
  color: white;
  margin-right: 10px;
}
.chat-input {
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-top: 1px solid #C1C1C1;
  padding: 10px 20px;
}
.chat-input input[type="text"] {
  border: none;
  outline: none;
  font-size: 14px;
  padding: 8px;
  flex: 1;
  margin-right: 10px;
  background-color: #F0F0F0;
  border-radius: 3px;
}
.chat-input input[type="text"]::placeholder {
  color: #AAA;
}
.chat-input .send-message-btn {
  border: none;
  outline: none;
  background-color: #008CBA;
  color: white;
  padding: 8px;
  border-radius: 3px;
  cursor: pointer;
  transition: all ease-in-out 0.2s;
}
.chat-input .send-message-btn:hover {
  background-color: #005F6B;
}

除了基本的布局之外,我们还需要通过 CSS 样式来进行美化,让聊天对话框看起来更具有交互性。 样式段落中定义了对话框的基本样式以及聊天记录、用户发送内容的美化样式。需要注意的是,为了更好的用户体验,我们需要将聊天记录容器 chat-history 设置为自动滚动,确保用户一直能看到最新的消息内容,同时也避免了消息被屏幕遮盖的问题。

三、小技巧

1、消息的自动滚动

this.$nextTick(() => {
  this.$refs.chatHistory.scrollTop = parseInt(window.getComputedStyle(this.$refs.chatHistory).height);
});

sendMessage 方法中,我们需要将窗口滚动到最底部,以便用户能够看到最新的聊天记录。由于 Vue 动态渲染的特殊性,直接使用原生 JS 获取元素 height 值来进行 scrollTop 的赋值是行不通的,因为在代码尚未渲染完成时,我们得到的 height 是初始值 0。所以我们需要以异步代码的形式,通过 Vue 的 $nextTick 方法帮助我们等待元素渲染完成后再进行 scroll 操作,以获取正确的 height 值。

2、机器人自动回复

sendMessage() {
  // ...
  // 机器人自动回复
  setTimeout(() => {
    let replyMsg = {
      username: '机器人小随',
      time: new Date().toLocaleString(),
      body: '您的消息已收到,稍后将有专人为您服务。',
      isMyMessage: false
    };
    this.messages.push(replyMsg);
    this.$nextTick(() => {
      this.$refs.chatHistory.scrollTop = parseInt(window.getComputedStyle(this.$refs.chatHistory).height);
    });   
  }, 1000);
}

在用户发送内容之后,我们通常需要在服务器、数据库等后端系统处进行处理。但是在前端展示时,我们可以通过异步代码来使机器人自动回复用户的消息。在 sendMessage 方法中,我们需要使用 setTimeout 进行延时操作,并创建一个 replyMsg 对象,模拟机器人的回复消息,然后将其 pushmessages 序列中。最后,我们再次执行窗口滑动到最下方的操作,确保用户能够看到机器人回复的内容。

3、监听滚动事件

mounted() {
  this.$refs.chatHistory.addEventListener('scroll', this.handleScroll)
},
destroyed() {
  this.$refs.chatHistory.removeEventListener('scroll', this.handleScroll)
},
methods: {
  handleScroll() {
    let scrollTop = this.$refs.chatHistory.scrollTop;
    if (scrollTop === 0) {
      // 执行加载更多的操作
    }
  }
}

在一些场景下,我们可能需要用户滚动到聊天记录的顶部时,自动加载更多的历史聊天记录。Vue 提供了一种事件监听的方式,让我们可以轻松地实现该功能。在聊天对话框被挂载到 DOM 之后,我们可以通过 addEventListener 监听 chatHistory 元素的 scroll 事件。当 scroll 事件被触发时,我们可以通过判断 scrollTop 的大小来判断用户是否滑动到了顶部。如果 scrollTop 为 0,代表用户已经滑动到顶部了,此时可以执行加载更多的操作。需要注意,Vue 中,在组件销毁时需要在 destroyed 生命周期中移除 scroll 事件的监听。

4、处理超长聊天记录的断行

.chat-history {
  // ...
  word-break: break-all;
}

在用户发送的聊天记录中,如果有长时间的文本、链接等内容,我们需要进行处理,以保证 UI 的美观性和阅读性。在样式段落中,我们使用 CSS 的 word-break 属性进行处理:当聊天记录的文本元素宽度超过父元素宽度时,自动断行,并将一些单词或连接符号拆分到下一行。这样能够有效解决聊天记录文本溢出、遮盖的问题,提高聊天室的可用性。