前端面试宝典---项目难点2-智能问答对话框采用虚拟列表动态渲染可视区域元素(10万+条数据)

引言

在我参与智能问答项目中一个智能体回话并不会像豆包一样,每次新建会话都是是从头开始,而项目中你想创建新会话就像chatbox一样,是点击橡皮擦开启新的聊天上下文,但是直接的聊天记录依然存在,针对超过十万+条对话数据进行展示。会出现页面卡死的问题。

所以我们可以通过虚拟列表的形式展示我们的对话内容

javascript 复制代码
<template>
  <div class="container">
    <!-- 展示问答内容 -->
    <div class="chat-container" ref="chatContainer" @scroll="handleScroll">
      <!-- 优化后 -->
      <div
        class="virtual-placeholder"
        :style="{ height: `${chatListHeight}px` }"
      ></div>
      <div
        class="truth-box"
        :style="{ transform: `translateY(${scrollTop}px)` }"
      >
        <div
          class="chat-item-container"
          :style="{ transform: `translateY(${scrollTopItem}px)` }"
        >
          <div
            v-for="(item, index) in renderedItems"
            :key="index"
            class="chat-item"
            :style="{ height: `${item.height}px` }"
          >
            <div v-if="item.isUser" class="user-message">
              {{ item.content }}
            </div>
            <div v-else class="system-message">{{ item.content }}</div>
          </div>
        </div>
      </div>
      <!-- ------------------------------------------- -->
      <!-- 优化前 -->
      <!-- <div
        v-for="(item, index) in chatList"
        :key="index"
        class="chat-item"
        :style="{ height: `${item.height}px` }"
      >
        <div v-if="item.isUser" class="user-message">{{ item.content }}</div>
        <div v-else class="system-message">{{ item.content }}</div>
      </div> -->
    </div>

    <!-- 输入框与发送按钮 -->
    <el-row type="flex" justify="center">
      <el-col :span="18">
        <el-input v-model="inputValue" placeholder="请输入内容"></el-input>
      </el-col>
      <el-col :span="6">
        <el-button type="primary" @click="send">发送</el-button>
      </el-col>
    </el-row>
  </div>
</template>

<script>
export default {
  data() {
    return {
      inputValue: '',
      chatList: [],
      startIndex: 0,
      visibleCount: 20, // 可视区域内显示的条目数
      itemHeight: 100, // 假设每个条目平均高度为 100px
      scrollTop: 0,
    }
  },
  computed: {
    chatListLength() {
      return this.chatList.length
    },
    chatListHeight() {
      return (
        (this.chatList.reduce((acc, item) => acc + item.height, 0) *
          this.chatListLength) /
        this.chatListLength
      )
    },
    renderedItems() {
      console.log('renderedItems', this.startIndex)

      return this.chatList.slice(
        this.startIndex,
        this.startIndex + this.visibleCount
      )
    },
    startOffset() {
      return this.chatList
        .slice(0, this.startIndex)
        .reduce((acc, item) => acc + item.height, 0)
    },
    scrollTopItem() {
      return this.startOffset - this.scrollTop
    },
  },
  mounted() {
    this.chatList = new Array(100000).fill(0).map(() => {
      return {
        isUser: Math.random() > 0.5,
        content: '欢迎来到我们的帮助中心!',
        height: Math.random() * 100 + 50,
      }
    })
  },
  methods: {
    send() {
      if (this.inputValue.trim() !== '') {
        // 添加用户的输入到聊天列表
        this.chatList.push({
          isUser: true,
          content: this.inputValue,
          height: Math.random() * 100 + 50,
        })
        // 模拟系统回复
        setTimeout(() => {
          this.chatList.push({
            isUser: false,
            content: '你可以通过点击设置选项来修改账户信息。',
            height: Math.random() * 100 + 50,
          })
        }, 500)
        // 清空输入框
        this.inputValue = ''
        // 自动滚动到底部
        this.scrollToBottom()
      }
    },
    handleScroll() {
      const scrollTop = this.$refs.chatContainer.scrollTop
      this.scrollTop = scrollTop
      let currentStartIndex = 0
      let totalHeiight = 0
      for (let i = 0; i < this.chatList.length; i++) {
        const item = this.chatList[i]
        totalHeiight += item.height
        if (totalHeiight >= scrollTop) {
          currentStartIndex = i
          break
        }
      }
      this.startIndex = currentStartIndex >= 0 ? currentStartIndex : 0
    },
    scrollToBottom() {
      this.$nextTick(() => {
        const container = this.$refs.chatContainer
        // 正确设置滚动到底部
        container.scrollTop = container.scrollHeight
      })
    },
  },
}
</script>
<style lang="scss" scoped>
.container {
  display: flex;
  flex-direction: column;
  height: 100%;
}
.chat-container {
  flex: 1;
  height: 0;
  overflow-y: auto;
  position: relative;
  .truth-box {
    width: 100%;
    position: absolute;
    top: 0;
    left: 0;
    overflow: hidden;
    height: 100%;
  }
}
.chat-item-container {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
}
.chat-item {
}

.user-message {
  text-align: right;
  color: #409eff; // Element UI 蓝色
  background: pink;
  height: 100%;
}

.system-message {
  height: 100%;
  text-align: left;
  color: #67c23a; // Element UI 绿色
  background: gray;
}
</style>
相关推荐
大圣编程3 小时前
Java 多维数组详解
java·开发语言
DianSan_ERP6 小时前
电商架构演进:如何在高并发场景下实现多平台API的标准化履约?
运维·前端·网络·安全·架构·自动化
殳翰6 小时前
下服务器端开发流程及相关工具介绍(C++)
开发语言·c++
落寞的星星7 小时前
这个对象就包含了已经转换好的DFA和各种词法分析器运转所需要的参数。下一步,我们就可以用ScannerInfo对象创建出Scanner对象,请看下面的代码:
开发语言·c#
nothing&nowhere7 小时前
用 Python 做问卷数据清洗:无效样本检测与处理实战
开发语言·python·数据清洗·数据处理·问卷星·问卷星脚本·刷问卷
2601_961593427 小时前
Rust 开发环境配置繁琐?RustRover 开箱即用搞定编码调试
开发语言·后端·macos·rust
HZZD_HZZD8 小时前
DL/T 645-2026新国标深度解读与智能电表协议适配实战:从帧结构变化到Java采集器升级的全链路改造方案
java·开发语言
碎_浪9 小时前
给键盘党的英语记忆工具:我做了一款「打字背单词」桌面应用
前端·程序员·ai编程
阿成学长_Cain10 小时前
Linux dirs命令详解|Bash目录堆栈管理快速切换目录实战教程
linux·运维·前端·数据库
多加点辣也没关系10 小时前
JavaScript|第4章:类型转换
开发语言·javascript