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

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

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

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

npm install vue-virtual-scroll-list --save

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

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

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

完整示例如下:

父组件

<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>
handleUpdateCell(data: any){
  this.items[data.index][data.column] = data.value;
}

子组件

    <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 这个事件,它只是作为容器渲染了子组件。

本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:alixiixcom@163.com