You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue.js短信仪表盘加载100条用户数据性能优化求助

Vue短信仪表盘加载性能优化方案

问题背景

基于Vue.js开发的短信仪表盘应用,每页加载100条用户数据时耗时约1分钟,卡顿核心原因是每条数据对应的头部组件包含大量Element UI功能组件(如Popover、Button、Switch等),导致初始渲染DOM节点过多、计算量过大。已尝试v-if/v-show、计算属性优化,效果不明显。


针对性优化方案

1. 拆分组件并实现懒加载

将当前头部的复杂内容拆分为多个独立子组件(如ChatTags.vue、ChatActionMenu.vue、ChatUserInfo.vue),并使用Vue的异步组件实现按需加载,避免初始渲染时一次性创建100份复杂组件实例:

// 父组件中引入异步子组件
import { defineAsyncComponent } from 'vue'
const ChatActionMenu = defineAsyncComponent(() => import('./ChatActionMenu.vue'))

在模板中直接使用拆分后的子组件,降低初始渲染的组件初始化开销。

2. 替换为虚拟列表渲染

这是解决大量列表项性能问题最有效的方案,仅渲染当前视口内的列表项,而非全部100条:

  • 使用Vue官方生态的vue-virtual-scroller库,或VueUse的useVirtualList工具函数
  • 实现思路:计算可视区域内的数据范围,只渲染对应范围的头部组件,滚动时动态更新渲染列表,直接减少90%以上的DOM节点数量。

3. 延迟Popover/Popconfirm的内容渲染

当前每条数据的头部包含多个el-popover/el-popconfirm,这些组件默认会提前渲染弹窗内容到DOM中,100条数据会生成大量隐藏DOM节点:

  • 给el-popover添加lazy属性,仅在触发时才渲染弹窗内容:
<el-popover placement="bottom-end" :width="150" trigger="click" lazy>
  • 对于自定义弹窗,结合teleport和v-if控制,仅在触发时渲染弹窗到body:
<el-button @click="showMenu = !showMenu">更多</el-button>
<teleport to="body">
  <div v-if="showMenu" class="action-menu-popup">
    <!-- 菜单内容 -->
  </div>
</teleport>

4. 预计算并缓存模板内的重复计算项

模板内存在多处重复计算(如item.phoneNumber.slice(-10)、formatPhoneNumberWithoutStar调用),会导致每次渲染重复执行计算逻辑:

  • 在数据请求完成后,统一预处理数据,将重复计算的结果缓存到数据项中:
// 数据预处理示例
const processUserData = (rawData) => {
  return Object.values(rawData.data).map(item => {
    const shortPhone = item.phoneNumber.slice(-10)
    return {
      ...item,
      shortPhone,
      formattedPhone: formatPhoneNumberWithoutStar(item.phoneNumber),
      userInfo: registeredUserList[shortPhone] || null
    }
  })
}

模板中直接使用预计算的shortPhone、formattedPhone、userInfo,避免重复计算。

5. 使用v-memo缓存组件渲染结果(Vue 3+)

对于数据变化不频繁的头部组件,使用v-memo指定核心依赖项,仅当依赖项变化时才重新渲染组件:

<template v-memo="[item.phoneNumber, item.assignToUser, item.addedTags]">
  <!-- 头部组件的全部内容 -->
</template>

这能避免列表中其他数据变化时,当前头部组件无意义的重复渲染。

6. 优化事件绑定方式

当前每条数据的头部绑定了多个@click、@close事件,100条数据会生成大量事件监听器:

  • 采用事件委托,将事件绑定到列表父元素上,通过event.target判断触发源,减少监听器数量:
<!-- 父元素上绑定事件 -->
<div class="chat-list" @click="handleListClick">
  <!-- 每条数据的头部组件 -->
</div>
// 父组件中处理事件
const handleListClick = (e) => {
  const target = e.target.closest('[data-action]')
  if (!target) return
  const action = target.dataset.action
  const phoneNumber = target.closest('.chat-item').dataset.phone
  // 根据action执行对应逻辑
  if (action === 'pin-chat') {
    mfToPinThisChat(phoneNumber)
  }
}

7. 优化数据查找效率

当前模板中通过registeredUserList[item.phoneNumber.slice(-10)]多次查找用户信息,可提前将用户信息关联到对应数据项中(如步骤4的预处理),或把registeredUserList转换为Map结构提升查找效率:

const userMap = new Map(Object.entries(registeredUserList))
// 查找时直接使用
const userInfo = userMap.get(shortPhone)

内容的提问来源于stack exchange,提问作者Sreeparna Das

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 02:02:01