vue-virtual-scroll-list虚拟组件实现思路详解

以下是"vue-virtual-scroll-list虚拟组件实现思路详解"的攻略:

什么是vue-virtual-scroll-list

vue-virtual-scroll-list 是一个基于 Vue.js 的虚拟滚动列表组件。 它通过渲染一部分可见的滚动视图,并随着滚动将视图进行重用,从而提高了大型数据列表的性能。

如何使用vue-virtual-scroll-list

vue-virtual-scroll-list 的使用非常简单,只需要通过npm安装组件,然后在Vue组件内引用即可:

npm install vue-virtual-scroll-list --save
<template>
  <div>
    <virtual-scroll-list :size="50" :remain="10" :bench="20" :tolerance="0" :list="longList">
      <template slot-scope="{ item }">
        <div>{{ item }}</div>
      </template>
    </virtual-scroll-list>
  </div>
</template>

<script>
import VirtualScrollList from 'vue-virtual-scroll-list'

export default {
  components: {
    VirtualScrollList
  },
  data () {
    return {
      longList: [/* 一大堆数据 */]
    }
  }
}
</script>

上述代码中,我们先引入一个 VirtualScrollList 组件,然后在模板中使用这个组件,并通过 list 属性将数据传入组件中。在 <template> 中使用了 slot-scope 来对每一项数据 item 进行自定义渲染。

vue-virtual-scroll-list 的实现思路

vue-virtual-scroll-list 的实现非常巧妙,主要是通过一些算法和策略来实现的。下面我们就来详细解析一下其实现思路:

1. 准备工作

在渲染列表前,我们需要先定义一些参数。

  • scroll: 此参数是列表容器,通过获取此元素的 scrollTop 和 offsetHeight 属性来监测滚动位置和容器高度。
  • cache: 一个对象缓存了所有已经渲染的子元素,以及它们的位置和大小信息,后面滚动到哪里,都不需要重新渲染和计算这些子元素的大小和位置。
data() {
  return {
    scroll: null,
    cache: {
      start: 0,
      end: -1,
      size: 0
    },
  }
},
mounted () {
  this.scroll = this.$refs.outer // 获取滚动元素引用
  this.scroll.addEventListener('scroll', this.handleScroll) // 监听滚动时间
  this.animationFrame = window.requestAnimationFrame(this.handleScroll) // 强制渲染第一屏
  this.cache = this.initCache()
},
methods: {
  handleScroll() {/*...*/}
  initCache() {/*...*/}
}

2. 初次渲染

当组件第一次渲染时,我们不得不渲染出整个列表,并记录下每一个子元素的大小和位置信息。这一步会比较耗性能,但它只需要执行一次,所以耗时可以接受。

initCache() {
  const ref = this.$refs.content
  const total = this.list.length

  let heights = new Array(total)
  let positions = new Array(total)
  let accHeight = 0

  for (let i = 0; i < total; i++) {
    const height = ref.children[i].offsetHeight
    heights[i] = height
    positions[i] = accHeight
    accHeight += height
  }

  return {
    start: 0,
    end: this.bench, // 预切换点
    heights,
    positions,
    size: accHeight
  }
}

initCache 函数中,我们通过循环记录每一个子元素的高度和位置信息,然后存储在 cache 变量中。startend 表示当前页面中渲染的子元素范围,heightspositions 分别表示每一个子元素的高度和相对于滚动容器顶部的位置。

3. 视图切换

一旦列表开始滚动,就会触发 handleScroll 方法。我们会在这个方法中计算当前需要渲染的子元素范围,并将未滚动到的子元素从页面中移除。

handleScroll() {
  const { start, end, cache } = this.cache
  const outerHeight = this.scroll.offsetHeight
  const scrollTop = this.scroll.scrollTop
  const containerTop = this.$refs.wrapper.getBoundingClientRect().top
  const threshold = this.tolerance // 阀值

  let newStart = start
  let newEnd = end

  if (scrollTop < cache.positions[start] - containerTop) { // 向上拖拽
    newStart = this.findStartIndex(scrollTop - containerTop)
    if (newStart < start) { // 省去无效渲染
      newEnd = Math.min(
        Math.max(newStart + this.remain, end),
        cache.positions.length - 1
      )
    }
  } else if (scrollTop + outerHeight > cache.positions[end] - containerTop + threshold) { // 向下拖拽
    newEnd = this.findEndIndex(scrollTop + outerHeight - containerTop + threshold)
    if (newEnd > end) { // 省去无效渲染
      newStart = Math.max(
        Math.min(newEnd - this.remain, start),
        0
      )
    }
  }

  // 将未显示的元素移除
  for (let i = start; i < newStart; i++) {
    this.removeItem(i)
  }
  for (let i = end; i > newEnd; i--) {
    this.removeItem(i)
  }

  // 将可见元素添加到页面中
  for (let i = newStart; i < start; i++) {
    this.createItem(i)
  }
  for (let i = end + 1; i <= newEnd; i++) {
    this.createItem(i)
  }

  // 更新 cache
  if (newStart !== start || newEnd !== end) {
    this.cache = {
      start: newStart,
      end: newEnd,
      size: cache.size,
      positions: cache.positions,
      heights: cache.heights
    }
  }
},

handleScroll 方法中,首先会计算处于可视区域内的子元素范围(newStartnewEnd)。如果可见区域发生了变化,我们需要将未展示的子元素从页面中移除,然后将可见的子元素添加至页面中。

4. 子元素重用

现在,我们已经解决了需要渲染哪些元素的问题。但是,每次滚动到新的区域时,需要重新渲染子元素是非常低效的。为了优化这一点,我们需要通过重用先前已经渲染的子元素来实现。

removeItem(index) {
  const node = this.$refs.content.children[index]
  this.placeholder.appendChild(node)
  this.cache.size -= this.cache.heights[index]
},

createItem(index) {
  const node =
    this.$refs[this.slotName] &&
    this.$refs[this.slotName][index] &&
    this.$refs[this.slotName][index][0] // 找到对应列表项的索引,通过v-show控制是否显示
  if (!node) return
  this.placeholder.appendChild(node.cloneNode(true))
  this.cache.size += this.cache.heights[index]
},

removeItemcreateItem 方法中,我们分别将子元素从页面中移除和添加回页面。需要注意的是,每一个移除的子元素,都要从 cache.size 中减去其自身的高度;每一个添加的子元素,都要从 cache.size 中加上其自身的高度。

示例说明

  • 示例1:假设需要渲染 10000 条数据,采用传统的渲染方式,肯定会卡顿。而使用 vue-virtual-scroll-list 虚拟列表组件,只会渲染出当前可视区域的元素,因此性能会大大提升。
  • 示例2:为了方便入门学习,我们可以参考 vue-virtual-scroll-list 源码,自己编写一个类似的虚拟列表组件。不仅能更深入理解其实现原理,还能够定制化更各式各样的外观和功能。

总结

以上就是 vue-virtual-scroll-list 虚拟列表组件的实现思路和代码示例。虽然组件实现思路有些复杂,但是通过实现,可以高效地渲染大量数据。在后续的开发过程中,可以参考和借鉴这样的思路,来处理大量数据的显示问题。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:vue-virtual-scroll-list虚拟组件实现思路详解 - Python技术站

(0)
上一篇 2023年5月27日
下一篇 2023年5月27日

相关文章

  • Vue不能watch数组和对象变化解决方案

    Vue中的watch属性用于监控变量的变化并执行相应的操作,但是Vue默认不能直接监控数组和对象的变化。如果要监控数组和对象的变化需要使用特定的解决方案。 问题分析 Vue默认不支持watch数组和对象的变化是因为Vue实现了一个高效的响应式系统,它利用了ES6的Proxy特性来实现对数据的监控。但是Proxy不支持ie11以下的浏览器,因此对于不支持ES6…

    Vue 2023年5月28日
    00
  • vue 获取及修改store.js里的公共变量实例

    获取和修改vue中的全局变量通常需要使用Vuex。Vuex是一个专业用于管理Vue应用中的状态(state)的库。它支持将状态集中在单个位置中,以便更轻松地进行管理和交互。 下面是如何在vue中获取和修改store.js里的公共变量实例的详细攻略: 安装依赖 为了使用Vuex,您需要先安装它。您可以在项目中使用以下命令安装它: npm install vue…

    Vue 2023年5月27日
    00
  • 自定义Vue组件打包、发布到npm及使用教程

    下面是详细的“自定义Vue组件打包、发布到npm及使用教程”的完整攻略: 一、前置准备 在开始之前,你需要确保以下几点已经完成: 已安装 Node.js 已安装 Vue CLI 已注册 NPM 账号并登录 二、创建 Vue 组件 创建 Vue 项目 首先,我们需要使用 Vue CLI 快速创建一个 Vue 项目。在命令行中运行以下命令: vue create…

    Vue 2023年5月28日
    00
  • 关于Vue组件库开发详析

    关于Vue组件库开发详析 Vue.js是一个流行的JavaScript框架,可以用于构建交互式Web界面。Vue组件库是我们可以在Vue应用程序中重复使用的一组可组合UI元素。 为什么要开发Vue组件库 提高开发效率:使用Vue组件库可以减少代码开发时间,提高开发效率,也有助于保持一致的UI风格。 易于维护:Vue组件库强制出现接口,降低维护成本,提高可重用…

    Vue 2023年5月27日
    00
  • vue 指令与过滤器案例代码

    以下是关于 Vue 指令与过滤器的详细攻略: Vue 指令 Vue.js 中的指令是一种特殊的属性,以 v- 开头,并且会在渲染时根据一些逻辑被解析和执行。指令主要是用来操作 DOM 元素的,包括变更元素的文本内容、监听元素的事件、控制元素的显示和隐藏等等。下面是几个常用的指令示例。 v-text 指令 这个指令可以用来替代元素的 innerText 属性,…

    Vue 2023年5月27日
    00
  • vue v-for循环出来的数据动态绑定值问题

    当使用Vue的v-for指令循环展示数据时,我们需要注意数据与状态的动态绑定问题。本文将详细讲解使用v-for指令循环展示数据时,因为数据改变而导致状态动态绑定的问题及解决方法。 问题现象 假设有如下一段v-for指令: <div v-for="item in items"> <input type="text…

    Vue 2023年5月29日
    00
  • vue上传文件formData入参为空,接口请求500的解决

    针对”vue上传文件formData入参为空,接口请求500″这一问题,可以按照以下步骤来进行排查和解决: 1.确保上传接口正确 首先需要确认上传接口是否能够正常处理上传请求,可以使用其他工具或方式来简单测试上传接口是否正常。如果上传接口正确无误,那么可以继续下一步排查。 2.确认请求头信息 当使用formData方式上传文件时,需要设置请求头信息,其中包括…

    Vue 2023年5月28日
    00
  • 如何正确理解vue中的key详解

    下面我将为大家详细讲解关于“如何正确理解Vue中的key”的攻略。 什么是key? 在Vue中,每个节点都需要有唯一的key属性,用于辅助Vue渲染虚拟DOM和更新真实DOM。 key的作用 提高Vue性能:在更新虚拟DOM时,Vue会基于key的变化来判断节点的位置以及是否需要重新渲染。有了key,在更新虚拟DOM时,Vue就可以精准地判断出新旧节点是否相…

    Vue 2023年5月29日
    00
合作推广
合作推广
分享本页
返回顶部