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

Vuetify v3中v-combobox加载10万条数据卡顿,求懒加载方案

Vuetify v3 v-combobox 十万条数据卡顿解决(懒加载/虚拟滚动方案)

直接把十万条数据传给v-combobox的items属性,会导致组件一次性渲染上万条DOM节点,直接卡死浏览器。下面给出几种可行的解决思路:

方案一:用虚拟滚动减少DOM渲染量

Vuetify的v-virtual-scroll组件可以只渲染当前视口内的列表项,不管数据量多大,DOM元素数量都维持在几十条,彻底解决卡顿。通过自定义v-combobox的menu插槽来实现:

<v-col v-for="property in filterableProperties" :key="property.code">
  <v-combobox 
    v-model="filter[property.code]" 
    :label="property.label" 
    variant="outlined" 
    hide-details 
    bg-color="white" 
    clearable
    :menu-props="{ maxHeight: 400 }"
  >
    <!-- 自定义下拉菜单内容,替换默认列表 -->
    <template v-slot:menu="{ isOpen }">
      <v-virtual-scroll
        v-if="isOpen"
        :items="values[property.code]"
        :item-height="48" <!-- 匹配v-list-item默认高度 -->
        height="400"
      >
        <template v-slot:item="{ item }">
          <v-list-item 
            :title="item" 
            @click="selectItem(property.code, item)"
          />
        </template>
      </v-virtual-scroll>
    </template>
  </v-combobox>
</v-col>

对应的脚本逻辑(手动处理选中赋值):

import { ref } from 'vue';

const filter = ref({});

function selectItem(code, item) {
  filter.value[code] = item;
}

方案二:搜索触发懒加载(按需过滤数据)

如果不需要展示全部数据,只需要根据用户输入的关键词返回匹配结果,可以监听v-combobox的search事件,每次仅过滤/加载少量数据:

<v-col v-for="property in filterableProperties" :key="property.code">
  <v-combobox 
    v-model="filter[property.code]" 
    :label="property.label" 
    :items="filteredValues[property.code]"
    variant="outlined" 
    hide-details 
    bg-color="white" 
    clearable
    :loading="loading[property.code]"
    @search="handleSearch(property.code, $event)"
  />
</v-col>

脚本逻辑:

import { ref } from 'vue';

const filter = ref({});
const filteredValues = ref({});
const loading = ref({});
const pageSize = 20; // 每次加载20条数据

// 初始化每个属性的过滤列表状态
filterableProperties.forEach(prop => {
  filteredValues.value[prop.code] = [];
  loading.value[prop.code] = false;
});

function handleSearch(code, searchQuery) {
  loading.value[code] = true;
  
  // 这里可以替换为后端接口请求,传入searchQuery和页码
  setTimeout(() => {
    const allItems = values[code];
    // 根据搜索词过滤数据,仅返回前pageSize条
    const matchedItems = allItems.filter(item => 
      item.toLowerCase().includes(searchQuery.toLowerCase())
    ).slice(0, pageSize);
    
    filteredValues.value[code] = matchedItems;
    loading.value[code] = false;
  }, 100);
}

方案三:虚拟滚动 + 滚动加载更多(过滤后数据仍较多的场景)

如果搜索后的匹配结果仍有几千条,可以结合虚拟滚动和滚动触底加载,进一步优化:

<v-col v-for="property in filterableProperties" :key="property.code">
  <v-combobox 
    v-model="filter[property.code]" 
    :label="property.label" 
    variant="outlined" 
    hide-details 
    bg-color="white" 
    clearable
    :menu-props="{ maxHeight: 400 }"
    :loading="loading[property.code]"
    @search="handleSearch(property.code, $event)"
  >
    <template v-slot:menu="{ isOpen }">
      <v-virtual-scroll
        v-if="isOpen"
        :items="filteredValues[property.code]"
        :item-height="48"
        height="400"
        @scroll="handleScroll(property.code, $event)"
      >
        <template v-slot:item="{ item }">
          <v-list-item 
            :title="item" 
            @click="selectItem(property.code, item)"
          />
        </template>
        <!-- 加载状态提示 -->
        <template v-slot:append>
          <v-progress-linear 
            v-if="loading[property.code]" 
            indeterminate 
            class="ma-2"
          />
        </template>
      </v-virtual-scroll>
    </template>
  </v-combobox>
</v-col>

补充脚本逻辑:

import { ref } from 'vue';

const filter = ref({});
const filteredValues = ref({});
const loading = ref({});
const currentPage = ref({});
const pageSize = 20;

filterableProperties.forEach(prop => {
  filteredValues.value[prop.code] = [];
  loading.value[prop.code] = false;
  currentPage.value[prop.code] = 1;
});

function selectItem(code, item) {
  filter.value[code] = item;
}

function handleSearch(code, searchQuery) {
  currentPage.value[code] = 1;
  loading.value[code] = true;
  
  setTimeout(() => {
    const allItems = values[code];
    const matchedItems = allItems.filter(item => 
      item.toLowerCase().includes(searchQuery.toLowerCase())
    );
    filteredValues.value[code] = matchedItems.slice(0, pageSize);
    loading.value[code] = false;
  }, 100);
}

function handleScroll(code, event) {
  const container = event.target;
  // 判断是否滚动到接近底部(预留10px的误差)
  if (container.scrollTop + container.clientHeight >= container.scrollHeight - 10) {
    if (loading.value[code]) return;
    
    loading.value[code] = true;
    currentPage.value[code]++;
    
    setTimeout(() => {
      const allItems = values[code];
      const matchedItems = allItems.filter(item => 
        item.toLowerCase().includes(filter.value[code]?.toLowerCase() || '')
      );
      // 计算下一页数据的起始和结束索引
      const start = (currentPage.value[code] - 1) * pageSize;
      const end = currentPage.value[code] * pageSize;
      const nextPageItems = matchedItems.slice(start, end);
      
      filteredValues.value[code] = [...filteredValues.value[code], ...nextPageItems];
      loading.value[code] = false;
    }, 100);
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 21:25:34