后端返回list前端如何处理_如何处理前端超长列表

本文探讨了后端返回大量数据时,前端处理超长列表的方法,包括懒加载和虚拟渲染。重点介绍了虚拟渲染的实现,如vue-virtual-scroller和react-virtualized,以及解决滚动不连贯、减少reflow、动态高度计算等问题的策略。最后,提到了ResizeObserver和第三方库在复杂高度变化场景的应用。
摘要由CSDN通过智能技术生成

3b3f35e13849eac35c0d4644b4a1475a.png

背景:系统中有一个添加品牌的搜索框,当搜索类目不做限制的时候,全部的品牌列表会有1W多个,这时候在框架的加持下,操作速度感人。可以在https://codesandbox.io/s/pure-vue-kqber中体验一下,甚至不用打开控制台看console输出,就可以感受到载入长列表和重置之间切换时,页面停止响应的时间。

问题产生原因

DOM节点数量过多,浏览器渲染吃力

1876afc3b2dd10d936cfa1fc562e5b89.png
(图片引用自https://zhuanlan.zhihu.com/p/26022258)

其实不光是初次渲染时间长,如果有大量节点出现,那么在滚动的时候,也能明显感受到不流畅的滚动现象。

可选方案

懒加载

通过懒加载的方式,在出现长列表的时候,第一次并不完全渲染所有的DOM节点,即可解决一部分场景下的问题。

优点:实现简单

缺点:

  1. 想要定位到某一个位置的数据会非常困难
  2. 如果一直加载到底,那么最终还是会出现大量的DOM节点,导致滚动不流畅

虚拟渲染

懒加载无法满足真正的长列表展示,那么如果真正要解决此类问题该怎么办?还有一种思路就是:列表局部渲染,又被称为虚拟列表.

当前比较知名的一些第三方库有vue-virtual-scroller、react-tiny-virtual-list、react-virtualized。它们都可以利用局部加载解决列表过长的问题的,vue-virtual-scroller、react-tiny-virtual-list一类的方案只支持虚拟列表,而react-virtualized这种大而全的库则是支持表格、集合、列表等多种情况下的局部加载方案。

单纯列表虚拟渲染

我们先看下vue-virtual-scroller、react-tiny-virtual-list这种纯虚拟列表的解决方案。它们的实现原理是利用视差和错觉制作一份出一份“虚拟”列表,一个虚拟列表由三部分组成:

  1. 视窗口
  2. 虚拟数据列表(数据展示)
  3. 滚动占位区块(底部滚动区)

虚拟列表侧面图示意:

a18c895dc825afc6998afdb502831de3.png

正面图:

f3867844533ad808b7b4192e4b22533d.png

滚动一段距离后:

d2181e9a7659186ec35d081c4eb75989.png

最终要实现的效果:由滚动占位区块产生滚动条,随着滚动条的移动,在可视窗口展示虚拟数据列表

react-virtualized的二维虚拟渲染

react-virtualized的实现方案和我们上面探讨的不太一样,因为表格是二维的,而列表是一维的(可以认为列表是一种特殊的表格),react-virtualized就是在二维的基础上构建的一套虚拟数据渲染工具。

示意图如下:

aa67b7c2c306ceddd36eb871804d3ff6.png

蓝色的部分被称为Cell,上面白色线分隔的区块叫做Section。

基本原理:在列表的上方打上一层方格(Section),下面的每个元素(Cell)都能落到某个方格上(Section)。滚动的时候,随着Cell的动态增加,Section也会被动态的创建,将每一个Cell都注册到对应的Section下。根据当前滚动到的Section,可以得到当前Section下包含的Cell,从此将Cell渲染出来。

/*
  0 1 2 3 4 5
 ┏━━━┯━━━┯━━━┓
0┃0 0┊1 3┊6 6┃
1┃0 0┊2 3┊6 6┃
 ┠┈┈┈┼┈┈┈┼┈┈┈┨
2┃4 4┊4 3┊7 8┃
3┃4 4┊4 5┊9 9┃
 ┗━━━┷━━━┷━━━┛
Sections to Cells map:
 0.0 [0]
 1.0 [1, 2, 3]
 2.0 [6]
 0.1 [4]
 1.1 [3, 4, 5]
 2.1 [7, 8, 9]
*/

本次实现方案

由于我们的目的是处理前端超长列表,而react-virtualized的实现方案是基于二维表格的,其List组件也是继承自Grid组件,如果要做列表方案,必须先实现二维的Grid方案。只处理长列表的情况下,实现一个单纯的虚拟列表渲染方案比二维的Grid方案要更合适一些。

基本结构

首先我们按照虚拟列表示意图来规划出若干个元素。.virtual-scroller乃整个滚动列表组件,在最外层监测其滚动事件。在内部我们需放置一个.phantom来撑开容器,使滚动条出现,并且该元素的高度 = 数据总数 * 列表项高度。接着我们在.phantom的上一层,再画出一个ul列表,它被用来动态加载数据,而它的位置和数据将由计算得出。

https://codesandbox.io/s/list--scrollbar-basic-bbxlq

<template>
  <div id="app">
    <input type="text" v-model.number="dataLength">条
    <div class="virtual-scroller" @scroll="onScroll" :style="{height: 600 + 'px'}">
      <div class="phantom" :style="{height: this.dataLength * itemHeight + 'px'}">
        <ul :style="{'margin-top': `${scrollTop}px`}">
          <li v-for="item in visibleList" :key="item.brandId" :style="{height: `${itemHeight}px`, 'line-height': `${itemHeight}px`}">
            <div>
              <div>{
    {item.name}}</div>
            </div>
          </li>
        </ul>
      </div>
    </div>
  </div>
</template>

<script>
export default {
    
  name: "App",
  data() {
    
    return {
    
      itemHeight: 60,
      visibleCount: 10,
      dataLength: 100,
      startIndex: 0,
      endIndex: 10,
      scrollTop: 0
    };
  },
  computed: {
    
    dataList() {
    
      const newDataList = [...Array(this.dataLength || 0).keys()].map((v, i) => ({
    
        brandId: i + 1,
        name: `第${
    i + 1}项`,
        height: this.itemHeight
      }));
      return newDataList;
    },
    visibleList() {
    
      return this.dataList.slice(this.startIndex, this.endIndex);
    }
  },
  watch: {
    
    dataList() {
    
      console.time('rerender&#
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值