学会前端虚拟滚动,看这篇就够了

一. 虚拟滚动是什么?

前端虚拟滚动是一种用于优化长列表或大量数据展示的技术。它的主要原理是只渲染用户当前可见区域的数据,而不是一次性渲染整个列表或数据集。

在传统的列表渲染中,如果数据量很大,渲染所有数据可能会导致性能下降,特别是在滚动时。虚拟滚动通过动态计算可见区域的位置和需要渲染的数据范围,只渲染可见区域内的部分数据,并在滚动时实时更新渲染的内容。

总结下就是:

  1. 接口返回了大量数据,前端将大量数据保存起来;
  2. 虚拟滚动的策略是:因为无论你返回了多大的数据量,用户可以看到的永远只是可视区域展示的数据,只是大量数据的很少一部分,所以保证可视区域的数据正常渲染就行。

二. 虚拟滚动怎么实现?

先上代码直观体验下

index.vue

<template>
  <div class="container">
    <my-virtual-scroller :items="list" />
  </div>
</template>

<script>
import myVirtualScroller from './myVirtualScroller.vue'
// 模拟一个长列表
const list = []
for(let i=0; i<10000; i++) {
  list.push({
    id: i,
    label: `virtual-list ${i}`
  })
}
export default {
  components: {
    myVirtualScroller
  },
  data() {
    return {
      list: list
    }
  }
}
</script>

<style scoped>
.container {
  height: 300px;
  border: 1px solid #ccc;
}
</style>


myVirtualScroller.vue

// 传入数据可以实现虚拟滚动事件
// 这是一个封装好的子组件 传入数组进行渲染
<template>
  <!-- 最底层的可视区容器 -->
  <div ref="list" class="infinite-list-container" @scroll="scrollEvent($event)">
    <!-- 中间的可滚动区域,z-index=-1,高度和真实列表相同,目的是出现相同的滚动条 -->
    <div
      class="infinite-list-phantom"
      :style="{ height: listHeight + 'px' }"
    ></div>
    <!-- 最上层的可视区列表,数据和偏移距离随着滚动距离的变化而变化 -->
    <div class="infinite-list" :style="{ transform: getTransform }">
      <div
        class="infinite-list-item"
        v-for="item in visibleData"
        :key="item.id"
        :style="{ height: itemSize + 'px' }"
      >
        {{ item.label }}
      </div>
    </div>
  </div>
</template>
 
<script>
export default {
  name: "MyVirtualList",
  props: {
    //父组件传过来的数据
    items: {
      type: Array,
      default: () => [],
    },
    //可视区域每一项的高度 没有传参的话默认高度是100px
    itemSize: {
      type: Number,
      //没有高度的话 高度会默认是100px
      default: 100,
    },
  },
  computed: {
    //列表总高度
    listHeight() {
      // 计算当前传值数据的总长度 * 每个盒子的高度 计算出所有数据的整体高度
      return this.items.length * this.itemSize;
    },
    //可视区列表的项数
    visibleCount() {
      return Math.ceil(this.screenHeight / this.itemSize);
    },
    //可视区列表偏移距离对应的样式
    getTransform() {
      return `translate3d(0,${this.startOffset}px,0)`;
    },
    //获取可视区列表数据
    visibleData() {
      // 截取出可视区域显示的盒子 进行渲染
      return this.items.slice(
        this.start,
        Math.min(this.end, this.items.length)
      );
    },
  },
  mounted() {
    // 获取可是区域的高度
    this.screenHeight = this.$refs.list.clientHeight;
    // 开始
    this.start = 0;
    // 结束
    this.end = this.start + this.visibleCount;
  },
  data() {
    return {
      screenHeight: 0, //可视区域高度
      startOffset: 0, //偏移距离
      start: 0, //起始索引
      end: 0, //结束索引
    };
  },
  methods: {
    scrollEvent() {
      //当前滚动条滚动位置
      let scrollTop = this.$refs.list.scrollTop;
      //此时的开始索引 使用当前滚动条高度 / 盒子高度 拿到的是当前是哪一个盒子为第一个
      this.start = Math.floor(scrollTop / this.itemSize);
      //此时的结束索引
      //获取到当前第一个盒子的数值 加 计算好可视区域可容纳多少盒子的值
      this.end = this.start + this.visibleCount;
      //此时的偏移距离
      this.startOffset = scrollTop - (scrollTop % this.itemSize);
  
    },
  },
};
</script>
 
<style scoped>
.infinite-list-container {
  height: 100%;
  overflow: auto;
  position: relative;
}
 
.infinite-list-phantom {
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
}
 
.infinite-list {
  left: 0;
  right: 0;
  top: 0;
  position: absolute;
}
 
.infinite-list-item {
  line-height: 50px;
  text-align: center;
  color: #555;
  border: 1px solid #ccc;
  box-sizing: border-box;
}
</style>

虚拟滚动的思路总结如下

1. 确定视窗位置:通过获取列表滚动区域的scrollTop属性,确定当前视窗的位置;

2. 计算需要渲染的节点:根据视窗位置和子节点的高度,计算出需要真实渲染的节点范围。可以使用Math.floor(scrollTop/itemHeight)和Math.ceil(viewHeight/itemHeight)来确定起始索引和渲染的节点数量;

3. 移动渲染的节点到视窗内:将需要渲染的节点移动到视窗内,可以通过修改节点的位置或使用其他方式实现;

4. 按需加载数据:根据滚动位置,动态加载相应的数据,只加载当前视窗可见范围内的数据,减少数据量和渲染负担;

5. 处理滚动事件:监听滚动事件,根据滚动位置实时更新需要渲染的节点和数据。

三. 虚拟滚动示例代码难点解析

1. 可视化容器为什么要包裹两个盒子,并且设置绝对定位?

第一个盒子(类名为"infinite-list-phantom")的作用:
其总高度为内容的总高度,主要要为了撑起父盒子,使父盒子有滚动条。
第二个盒子(类名为‘infinite-list’)的作用:
这个要可视区域,是用户所能看到的界面,也是数据渲染的界面。

至于为什么要设置绝对定位,是因为需要第二个盒子覆盖第一个盒子,以满足第二个盒子展现在可视界面。

2. 为什么要设置:style=“{ transform: getTransform }”?

原因如下:

  1. 因为可视界面区域有限,在滚动后,会展示总的内容区域和可视区域不相交的部分,在可视区域就会出现部分或全部空白。
  2. 滚动的过程本质要内容区域的位置变化,其变化的数值可以通过scrollTop来查看;
  3. 通过transform的作用,抵消scrollTop的位置变动,使得我们看到的还是原先的可视区域位置的dom节点,只不过在滚动的过程中,渲染数据会改变。

3.为什么要设置偏移距离?

相关代码如下:

 //此时的偏移距离
 this.startOffset = scrollTop - (scrollTop % this.itemSize);

原因如下:

  1. 为了使得滚动的时候有动态效果,体验会更好;
  2. 可以尝试将scrollTop - 改成 scrollTop + 测试,就可以更好的去理解2、3点疑问。
  • 25
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
学习webpack是为了掌握前端代码打包工具的使用。随着前端工程化的发展,webpack已经成为了前端开发中最常用的打包工具之一。学习webpack可以帮助我们对项目的模块化管理、代码压缩与优化、资源加载等方面进行更加灵活和高效的处理。 这篇文章主要介绍了webpack的基础知识和使用方法。首先,它解释了webpack的基本概念,如入口和出口文件、loader和plugin等。然后,它详细介绍了如何配置webpack的各个参数和插件,包括如何使用Babel处理ES6语法,如何使用Less或Sass处理CSS,如何使用图片压缩插件等。 接着,文章讲解了webpack的打包原理和优化技巧。它介绍了webpack的模块化加载机制,以及如何使用Code Splitting和Dynamic Import等功能来提高页面加载速度。同时,它还提到了如何使用webpack进行代码分割和缓存优化,以及如何使用Tree Shaking和代码压缩插件来减小项目的体积。 最后,文章介绍了webpack的常见问题和解决方法。它列举了一些常见的错误和警告信息,并提供了相应的解决方案。此外,它还提供了一些常用的webpack插件和工具的推荐,帮助我们更好地进行前端开发。 总的来说,这篇文章提供了一个很好的入门教程,可以帮助我们快速掌握webpack的基本概念和使用方法。但是,要想在实际项目中灵活应用webpack,还需要深入学习官方文档和相关资料,并结合实际项目进行实践。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值