一篇文章理解虚拟滚动原理

这篇具有很好参考价值的文章主要介绍了一篇文章理解虚拟滚动原理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.为什么使用虚拟滚动?

首先提到一个现象,前端的性能瓶颈那就是页面的卡顿,当然这种页面的卡顿包含了多种原因。例如HTTP请求过多导致数据加载国漫,下载的静态文件非常大导致页面加载时间很长,js中一些算法响应的时间过长等。很多前端工程师都花费很多的精力在dom渲染上来优化页面加载。

2.浏览器渲染原理

在我们讨论今天的这个虚拟滚动原理之前需要了解一下浏览器的渲染原理。

浏览器渲染页面的过程分为以下几步:

  1. 解析html文件并生成 Dom Tree

  1. CSS解析生成CSS Rule Tree

  1. 在渲染阶段,浏览器会把DOM TreeCSS Rule TreeDOM Tree上的每个节点添加样式,并生成Render Tree

  1. Render Tree(layout/reflow),绘制元素尺寸、位置计算。

  1. 将计算好的信息发给GPU并显示在页面。

具体的浏览器原理在这篇文章不做过多的介绍,有兴趣的话可以去看我的另一篇文章《一篇文章理解浏览器渲染原理和机制》。

3.浏览器渲染瓶颈

首先大家要明重绘回流(重排)的概念:

  • 重绘(repaint):当Render Tree 中的一些元素需要更新元素本身的属性,只影响外观样式和颜色等,不影响整个布局。

  • 回流(reflow):当Render Tree 中的某些元素因为规模、尺寸、位置等改变时,会影响整个布局。

回流必定发生重绘重绘不一定发生回流,所以大家可以知道,回流所造成的影响是比较大的,如果页面中频繁的触发回流的操作,那么最终造成页面卡顿也是肯定的。

造成回流和重绘的操作有以下类别:

  • 页面初始化

  • 添加或者删除页面上的可视区DOM元素

  • 元素位置发生改变,定位和浮动,盒模型

  • 页面文本内容发生变化,影响输入框的大小改变。

  • 图片显示加载,如果没有加载图片又会被替换成相应提示文字信息。

  • 浏览器窗口尺寸大小变化(回流是根据视口大小来计算页面元素的位置和大小)。

其实对于这些需要考虑的因素,一些浏览器也是做出了相应的处理,因为每次回流可能会造成巨大的影响,浏览器本身会实现一个队列记录每次回流时操作,当存放的操作数量达到一定值或者达到一定时间后会对队列中的操作进行清空,并一次性进行一次回流,让多次回流操作压缩成一次回流操作执行,提高效率。

本文章将着重讲述关于滚动事件scroll event造成的影响。滚动事件本身不会造成太多的性能消耗,而是因为滚动事件伴随有大量的元素参与进来一起进行回流的操作才会影响浏览器的性能。例如一个表格有上万行数据,如果一次性展示在页面中,并且在滚动时显示对应偏移的数据,那么每一次滚动都会对这几万个元素进行回流,那么性能肯定会很差。

浏览器的瓶颈主要在于:

1.无法一次性渲染太多的DOM元素。

2.每次滚动事件将会让对应的DOM中所有元素重新渲染。

针对于浏览器的瓶颈问题,有三种解决办法:数据分页、无限滚动、虚拟滚动。

4.数据分页

许多网页和应用程序都会用到这样的方,对需要展示的大量数据进行分割分页,后端已经做好了分页,前端只需要调用后端的接口传入相应的第几页的参数就能获取到,减少了一次性需要渲染的行数,但是如果查询的表列数非常多,还是可能会渲染很多元素,不是一个很稳定的方法。

5.无限滚动

该方法是在页面渲染一次性所能成手最大范围的数据量,当滚动条快接近底部时,再去追加渲染下一批需要渲染的元素,但是该方法的明显缺血在于,如果数据量过大,无限滚动下去那么最终所造成渲染的元素越来越多,性能也不会很好。

6.虚拟滚动

虚拟滚动其实就是综合数据分页和无限滚动的方法,在有限的视口中只渲染我们所能看到的数据,超出视口之外的数据就不进行渲染,可以通过计算可视范围内的但单元格,保证每一次滚动渲染的DOM元素都是可以控制的,不会担心像数据分页一样一次性渲染过多,也不会发生像无限滚动方案那样会存在数据堆积,是一种很好的解决办法。

假设实际开发中服务端一次响应20万条列表数据,此时设备屏幕只允许容纳20条,那么用户理论上只可以看见20条数据,其他的数据不会进行渲染加载。如果前端将20万条数据全部渲染成DOM元素,可能造成程序卡顿,占用较大资源,非常影响用户体验,那么虚拟滚动技术就完美的解决了这一问题。

一篇文章理解虚拟滚动原理

如图所示,当我们进行滚动时,可视区域大小不变,渲染的元素数量也是可以控制的,合理的减少了不必要的DOM渲染,提高浏览器的性能。

可以计算:卷入行数 = scrollTop(卷入高度) / 每行的高度(itemH)

黄色边框内为可视区域,可视区域内的红色行表示在页面能展示的数据,每次滚动时,计算scrollTop的值,可视区域内的红色渲染部分高度可以略大于黄色边框可是高度,避免滚动的时候直接替换。

如何计算可视区域渲染的元素以及实现虚拟滚动,步骤如下:

  • 统一设置每一行的高度需要相同,方便计算。

  • 需要计算渲染数据数量(数组的长度),根据每行的高度以及元素的总量计算整个DOM渲染容器的高度。

  • 获取可视区域的高度

  • 触发滚动事件后,计算偏移量(滚动条据顶距离),再根据可视区域高度计算本次偏移的截止量,得到需要渲染的具体数据。

  • 对于与表格的列来说,需要做虚拟滚动的话,在x轴同样可以根据以上步骤执行,实现横向虚拟滚动。

7.自定义封装一个虚拟滚动组件:

子组件:

<template>
  <!-- 可视区盒子 -->
  <div :style="`height:${viewH}px;overflow-y:scroll`"
       @scroll="handleScroll"
       class="container">
    <div :style="`height:${scrollH}px`"
         class="list">
      <div class="item_box"
           :style="`transform:translateY(${offsetY}px)`">
        <div class="item"
             :style="`height:${itemH}px`"
             v-for="(item,index) in list"
             :key="index">
          {{ item }}
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'ScrollComponent',
  props: {
    data: Array,   // 列表总数据
    viewH: Number, // 外部高度
    itemH: Number, // 单项高度
  },
  data () {
    return {
      scrollH: '', // 整个滚动列表高度(总高度)
      list: [],    // 每次显示的数据
      showNum: '', // 页面需要显示的数量
      offsetY: '',// 动态偏移量- 外层的盒子进行滚动设置
      lastTime: '', //最新的时间
    }
  },
  mounted () {
    // 初始化计算
    this.scrollH = this.data.length * this.itemH
    // 计算可视化高度中能存几个列表,可以略多余可视化高度能存放的列表数量避免滚动时被替换
    this.showNum = Math.floor(this.viewH / this.itemH) + 1
    // 默认展示的几个数据
    this.list = this.data.slice(0, this.showNum)
    this.lastTime = new Date().getTime()
  },
  methods: {
    // handleScroll 滚动时候触发回调
    handleScroll (e) {
      // 控制滚动时间间隔
      if (new Date().getTime() - this.lastTime > 10) {
        let scrollTop = e.target.scrollTop //滚动条高度
        // 每一次滚动后 根据scrollTop值获取一个可以整除itemH结果进行偏移
        // 例如:scrollTop = 1220,1220 % this.itemH = 20 offsetY = 1220-20 = 1200
        this.offsetY = scrollTop - (scrollTop % this.itemH)
        console.log('卷入scrollTop值:', scrollTop, '卷入的行数:', scrollTop % this.itemH);
        this.list = this.data.slice(
          Math.floor(scrollTop / this.itemH), // 计算卷入了多少行
          Math.floor(scrollTop / this.itemH) + this.showNum
        )
        this.lastTime = new Date().getTime() //更新最新时间
      }
    }
  }
}
</script>

<style scoped>
.container {
  position: relative;
  top: 200px;
  left: 500px;
  border: 1px solid red;
  width: 500px;
}
.item {
  border: 1px solid pink;
}
</style>

父组件:

<template>
  <div id="app">
    <ScrollComponent :data="dataList"
                     :viewH="viewH"
                     :itemH="itemH" />
  </div>
</template>

<script>
import ScrollComponent from './components/ScrollComponent.vue'

export default {
  name: 'App',
  components: {
    ScrollComponent
  },
  data () {
    return {
      dataList: [1, 1, 11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 11, 1, 1, 1, 1, 11, 1, 1, 1, 1, 1],
      viewH: 200,
      itemH: 40
    }
  },
  mounted () {

  }
}
</script>

<style>
* {
  margin: 0;
  padding: 0;
}
</style>

浏览器显示结果:文章来源地址https://www.toymoban.com/news/detail-452332.html

一篇文章理解虚拟滚动原理

到了这里,关于一篇文章理解虚拟滚动原理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Java】还不理解继承?一篇文章看懂继承|继承入门

    作者: 努力学习的大一在校计算机专业学生,热爱学习和创作。目前在学习和分享:算法、数据结构、Java等相关知识。 博主主页: @是瑶瑶子啦 所属专栏: Java岛冒险记【从小白到大佬之路】;该专栏专注于Java相关知识,持续更新,每一篇内容优质,浅显易懂,不失深度!

    2024年02月01日
    浏览(31)
  • C++初阶之一篇文章让你掌握vector(理解和使用)

    在C++中,std::vector是标准模板库(STL)中的一种动态数组容器,它可以存储任意类型的元素,并且能够自动调整大小。std::vector提供了许多方便的成员函数,使得对数组的操作更加简单和高效。 vector声明 : template class T, class Alloc = allocatorT ; 这是 std::vector 的一般模板定义。它

    2024年02月14日
    浏览(30)
  • MapReduce的工作原理这篇文章就够了

    MapReduce是一种分布式计算模型,用于处理大规模数据集。它将大规模数据集分成小的数据块,然后在分布式计算集群上并行处理这些数据块。MapReduce模型由Google公司提出,并在Hadoop等开源框架中得到了广泛应用。 MapReduce模型包含两个阶段:Map阶段和Reduce阶段。 Map阶段 在Map阶

    2024年02月06日
    浏览(30)
  • 【无标题】一篇文章带你彻底理解Java ArrayList数据结构详解

    基本概念: ​ **之前创建数组的时候,需要声明提前声明数组的大小,**ArrayList是一个可以动态修改的数组,与普通数组的区别就是没有固定大小的限制,它会动态调整长度。 ​ **ArrayList继承了AbstractList,并实现了List接口。**如下图: **ArrayList 类位于 java.util 包中,**使用前

    2024年02月14日
    浏览(40)
  • 一篇文章带你了解-selenium工作原理详解

    前言 Selenium是一个用于Web应用程序自动化测试工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Google Chrome,Opera等。 主要功能包括:测试与浏览器的兼容性——测试你的应用程序看是否能够很好得

    2024年02月10日
    浏览(36)
  • 一篇文章掌握WebService服务、工作原理、核心组件、主流框架

    目录 1、WebService定义 解决问题: 2、WebService的工作原理 2.1 实现一个完整的Web服务包括以下步骤 2.2 调用方式 3、Web Service的核心组件 3.1 XML 3.2 SOAP 3.3 WSDL 3.4 UDDI 4、主流框架 4.1 AXIS(已淘汰) 4.2 XFire 4.3 CXF 5、Soap协议详解 1.Soap协议是什么 2.认识Soap 3.结论 4.SOAP小总结 6、WSDL详解

    2024年01月18日
    浏览(39)
  • RabbitMQ如何保证消息可靠性,看完这篇文章佬会有新的理解

    前言:大家好,我是小威,24届毕业生,在一家满意的公司实习。本篇文章将详细介绍RabbitMQ的消息可靠性机制,如消息丢失,消息重复性消费,消息积压等问题。 如果文章有什么需要改进的地方还请大佬不吝赐教 👏👏。 小威在此先感谢各位大佬啦~~🤞🤞 🏠个人主页:小

    2024年02月03日
    浏览(40)
  • 一篇文章让你彻底了解vuex的使用及原理(上)

    文章讲解的 Vuex 的版本为 4.1.0 ,会根据一些 api 来深入源码讲解,帮助大家更快掌握 vuex 的使用。 使用 Vue 实例的 use 方法把 Vuex 实例注入到 Vue 实例中。 use 方法执行的是插件的中的 install 方法 src/store.js 从上面可以看到 Vue 实例通过 provide 方法把 store 实例 provide 到了根实例

    2023年04月23日
    浏览(47)
  • 一篇文章带你从入门都入土 Kafka 消息中间件(原理+代码)

    目录 一、Kafka定义 二、消息队列 三、Kafka基础架构图 四、安装Kafka 4.1 为每台服务器下载Kafka并解压 4.2 查看目录结构 4.3 为每台服务器修改配置文件server.properties 4.4 为每台服务器配置Kafka环境变量 4.5 启动zookeeper集群 4.6 启动Kafka集群 4.7 关闭Kafka集群的注意事项 五、Topic命令

    2024年02月04日
    浏览(27)
  • 【Spring框架】一篇文章带你彻底搞懂Spring解决循环依赖的底层原理

    目录 一、前言 二、什么是循环依赖 三、Spring Bean 的循环依赖问题 3.1 Bean 的创建步骤 3.2 为什么 Spring Bean 会产生循环依赖问题? 3.3 什么情况下循环依赖可以被处理? 四、Spring 如何解决循环依赖问题? 4.0 什么是三级缓存 4.1 简单的循环依赖(没有AOP) 4.1.0 创建Bean的前期流

    2024年04月17日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包