避免渲染大量数据造成页面卡顿——虚拟滚动

后端一次性返回太多数据需要前端全部渲染造成页面卡顿或者浏览器崩溃怎么办?

常见处理方式是和后端沟通数据分页,真正减少数据量,但若不具备此条件需要仅在前端处理或前端需要展示全部数量的情况,可以使用虚拟滚动的方式。

【需求】Vue项目,后端传过来的数据对象数组items, 在前端以表格展示,并且部分列可以修改,点击提交按钮将修改后的数据全部回传。

【实现】引入vue-virtual-scroll-list库,一个适配Vue2项目的轻量级虚拟滚动库

scss 复制代码
npm install vue-virtual-scroll-list --save

vue-virtual-scroll-list 提供了一个名为 virtual-list 的组件,你需要将它注册到你的 Vue 组件中,并传入三个核心属性:

  • data-key :数据项的唯一标识字段(如 id)。
  • data-sources:数据源数组(即你的 9000 条数据)。
  • data-component:用于渲染每个列表项的子组件。

同时,需要给 virtual-list 设置一个固定高度(或最大高度),并开启滚动。

完整示例如下:

父组件

ruby 复制代码
<div class="list-container">
    <virtual-list
        style="height: 500px; overflow-y: auto;"
        :data-key="'id'"
        :data-sources="items"
        :estimate-size="60"
        :keeps="30"
        :data-component="rowComponent"
        :extra-props="{ onUpdateCell: handleUpdateCell }"
    >
    </virtual-list>
</div>
kotlin 复制代码
handleUpdateCell(data: any){
  this.items[data.index][data.column] = data.value;
}

子组件

<template> 复制代码
>    <div class="tr">
        <div class="td" style="width: 100px;">{{ index }}</div>
        <div class="td" style="width: 200px;">{{ source.articleId }}</div>
        <div class="td" style="width: 400px;">{{ source.articleName }}</div>
        <div class="td" style="width: 100px;" @dblclick="startEdit('qty')">
            <input
                v-if="editingColumn === 'qty'"
                ref="input"
                v-model="editingValue"
                class="edit-input"
                @keyup.enter="saveEdit('qty',index)"
            />
            <span v-else>{{ source.qty }}</span>
        </div>

    </div>
</template>

<script>
export default {
  name: 'TableRow',
  props: {
    // virtual-list 会自动将数据项以 'source' 属性传递给子组件
    source: {
      type: Object,
      required: true
    },
    // 可选:索引值(如果需要在模板中显示索引)
    index: {
      type: Number
    },
    onUpdateCell: { type: Function, required: true }  // 接收父组件传递的方法
  },
  data() {
    return {
      editingColumn: null, // 编辑列
      editingValue: ''     // 临时编辑值
    };
  },
  methods: {
    // 双击进入编辑模式
    startEdit(column) {
      this.editingColumn = column;
      // 初始化临时值为当前字段值
      this.editingValue = this.source[column];
      // 自动聚焦输入框
      this.$nextTick(() => {
        if (this.$refs.input) {
          this.$refs.input.focus();
        }
      });
    },
    // 保存编辑
    saveEdit(column,index) {

      if (this.editingColumn !== column) return; // 防止误触发

      // 只有当值发生变化时才触发更新事件
      if (this.editingValue !== this.source[column]) {
        console.log('saveEdit',column,index,this.editingValue)
        // 直接调用父组件传递的方法,而不是 $emit
        this.onUpdateCell({
          index: index,
          column,
          value: this.editingValue
        });
      }
      // 退出编辑模式
      this.editingColumn = null;
    }
  }
};
</script>

<style scoped>
.tr {
  display: flex;           /* 使用 flex 保证每列宽度按设定分配 */
  width: 100%;
  border-bottom: 1px solid #eee;
}
.td {
  padding: 8px;
  box-sizing: border-box;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.editable-text {
  cursor: pointer;
  display: block;
  width: 100%;
}
.edit-input {
  width: 100%;
  padding: 4px;
  border: 1px solid #1890ff;
  border-radius: 2px;
  outline: none;
}
</style>

【注】

子传父不能用$emit,Vue 的自定义事件不会自动跨组件冒泡 ,因此父组件在 virtual-list 标签上监听 @update-cell 是无效的------virtual-list 本身并没有 $emit 这个事件,它只是作为容器渲染了子组件。

相关推荐
斯内普吖12 分钟前
(开源)水果蔬菜商城实战指南 基于 Java + SSM + Vue + MySQL
java·vue.js·mysql·开源
ss2731 小时前
AI全栈实战 | 2.2-02 React 思维模型:数据不可变 vs Vue 数据可变,两种哲学把复杂度推给谁
前端·vue.js·react.js
天衍四九-1 小时前
Docker Compose企业实战系列(三):Vue/React前端项目容器化完整部署
前端·vue.js·docker
墨鱼老师1 小时前
SpringBoot+Vue 毕设项目开发踩坑记录(前后端分离)
vue.js·spring boot·毕业设计·课程设计·前后端分离·java 毕设
雪芽蓝域zzs2 小时前
Vue3 + Element Plus el-select 下拉弹窗
vue.js·elementui
Wx-bishekaifayuan2 小时前
springboot房屋租赁系统11574-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·sql·spring·课程设计
小白的码BUG之路2 小时前
Docker -- 构建nginx镜像,启动vue项目
vue.js·nginx·docker
3Q工具箱 - BraggTroy3 小时前
Vue SPA 站百度收录“全军覆没“?一次预渲染抢救的完整实战(2026 踩坑版)
vue.js·百度·dubbo
FYKJ_201016 小时前
SSM校园失物招领系统41452-计算机课程设计、毕业设计
java·vue.js·spring boot·python·mysql·typescript·spark
FYKJ_20101 天前
springboot贺州地方新闻网站87812-计算机课程设计、毕业设计
java·vue.js·spring boot·python·mysql·django·课程设计