前端虚拟列表实现的思路及完整代码

 更新时间:2025年01月23日 09:54:06   作者:跳房子的前端  
这篇文章主要介绍了前端虚拟列表实现的思路及完整代码,虚拟列表是一种优化长列表性能的技术,通过只渲染可视区域的列表项来提升页面性能和用户体验,文中通过代码介绍的非常详细,需要的朋友可以参考下

一、什么是虚拟列表

虚拟列表(Virtual List)是一种用于优化长列表性能的技术解决方案。其核心思想是:只渲染可视区域的列表项,而不是渲染整个列表的所有项。这种技术特别适用于需要展示大量数据的场景,可以显著提升页面性能和用户体验。

二、为什么需要虚拟列表

假设我们需要渲染一个包含10000条数据的列表:

  • 性能问题

    • DOM节点过多会导致页面渲染慢
    • 内存占用大
    • 滚动卡顿
  • 用户体验

    • 首屏加载时间长
    • 操作响应慢
    • 设备发热严重

三、虚拟列表的实现原理

1. 核心概念

  • 可视区域(Viewport):用户能看到的列表区域
  • 可视区域高度:视口的高度
  • 列表项高度:每个列表项的高度
  • 可视区域起始索引:当前可见区域的第一个列表项的索引
  • 可视区域结束索引:当前可见区域的最后一个列表项的索引
  • 偏移量(offset):列表滚动的距离

2. 计算公式

// 可显示的列表项数量
visibleCount = Math.ceil(viewportHeight / itemHeight)

// 起始索引
startIndex = Math.floor(scrollTop / itemHeight)

// 结束索引
endIndex = startIndex + visibleCount

// 偏移量
offset = scrollTop - (scrollTop % itemHeight)

四、代码实现

1. 基础结构

<div class="viewport" @scroll="handleScroll">
  <div class="scroll-list" :style="{ height: totalHeight + 'px' }">
    <div class="scroll-content" :style="{ transform: `translateY(${offset}px)` }">
      <!-- 渲染区域列表项 -->
    </div>
  </div>
</div>

2. 完整实现

class VirtualList {
  constructor(options) {
    this.itemHeight = options.itemHeight;
    this.total = options.total;
    this.viewport = options.viewport;
    this.buffer = options.buffer || 5;
    
    this.viewportHeight = this.viewport.clientHeight;
    this.visibleCount = Math.ceil(this.viewportHeight / this.itemHeight);
    this.startIndex = 0;
    this.endIndex = this.startIndex + this.visibleCount + this.buffer;
    this.offset = 0;
    
    this.initDom();
    this.bindEvents();
  }
  
  initDom() {
    this.scrollList = document.createElement('div');
    this.scrollList.className = 'scroll-list';
    this.scrollList.style.height = this.total * this.itemHeight + 'px';
    
    this.scrollContent = document.createElement('div');
    this.scrollContent.className = 'scroll-content';
    
    this.scrollList.appendChild(this.scrollContent);
    this.viewport.appendChild(this.scrollList);
  }
  
  bindEvents() {
    this.viewport.addEventListener('scroll', this.handleScroll.bind(this));
  }
  
  handleScroll() {
    const scrollTop = this.viewport.scrollTop;
    this.startIndex = Math.floor(scrollTop / this.itemHeight);
    this.endIndex = this.startIndex + this.visibleCount + this.buffer;
    this.offset = this.startIndex * this.itemHeight;
    
    this.updateContent();
  }
  
  updateContent() {
    const visibleData = this.getVisibleData();
    this.scrollContent.style.transform = `translateY(${this.offset}px)`;
    // 更新列表内容
    this.renderData(visibleData);
  }
  
  getVisibleData() {
    return this.data.slice(this.startIndex, this.endIndex);
  }
  
  renderData(data) {
    // 根据数据渲染DOM
  }
}

五、优化技巧

1. 使用 Buffer 缓冲区

在可视区域的上下额外渲染一些列表项,可以改善快速滚动时的白屏问题:

const bufferSize = 5;
startIndex = Math.max(0, startIndex - bufferSize);
endIndex = Math.min(total, endIndex + bufferSize);

2. 动态高度处理

对于列表项高度不固定的情况,可以:

  • 预估高度
  • 缓存真实高度
  • 动态更新位置
class DynamicVirtualList {
  constructor() {
    this.heightCache = new Map();
    this.estimatedHeight = 100;
  }
  
  updatePosition() {
    let totalHeight = 0;
    for (let i = 0; i < this.startIndex; i++) {
      totalHeight += this.getItemHeight(i);
    }
    this.offset = totalHeight;
  }
  
  getItemHeight(index) {
    return this.heightCache.get(index) || this.estimatedHeight;
  }
}

3. 防抖和节流

对滚动事件进行防抖和节流处理,避免频繁计算:

function throttle(fn, delay) {
  let timer = null;
  return function() {
    if (!timer) {
      timer = setTimeout(() => {
        fn.apply(this, arguments);
        timer = null;
      }, delay);
    }
  }
}

handleScroll = throttle(function() {
  // 滚动处理逻辑
}, 16);

六、性能优化建议

  • 使用 transform 代替 top

    • transform 执行在合成层,性能更好
    • 避免重排
  • RAF优化

    requestAnimationFrame(() => {
      this.updateContent();
    });
    
  • 列表项组件缓存

    • 使用React.memo或Vue的keep-alive
    • 避免不必要的重渲染
  • 避免在滚动时进行大量计算

    • 预计算数据
    • 使用Web Worker

七、使用场景

  • 长列表渲染
  • 无限滚动
  • 大数据表格
  • 时间轴
  • 聊天记录

八、注意事项

  • 需要准确设置容器高度
  • 处理列表项动态高度
  • 考虑滚动到底部的加载更多
  • 保持滚动位置
  • 键盘访问性

这篇文章详细介绍了虚拟列表的概念、实现原理和优化方案。主要包括:

  • 基本概念和原理解释
  • 完整的代码实现
  • 多种优化方案
  • 实际应用场景
  • 注意事项

总结 

到此这篇关于前端虚拟列表实现的文章就介绍到这了,更多相关前端虚拟列表实现内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • vue中使用router全局守卫实现页面拦截的示例

    vue中使用router全局守卫实现页面拦截的示例

    这篇文章主要介绍了vue中使用router全局守卫实现页面拦截的示例,帮助大家维护自己的项目,感兴趣的朋友可以了解下
    2020-10-10
  • vue element-ui读取pdf文件的方法

    vue element-ui读取pdf文件的方法

    这篇文章主要介绍了vue element-ui读取pdf文件的方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-11-11
  • Vue项目利用axios请求接口下载excel

    Vue项目利用axios请求接口下载excel

    这篇文章主要为大家详细介绍了Vue项目利用axios请求接口下载excel,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-11-11
  • vue-cli2.x旧版本卸载不掉的问题踩坑指南(附Vue脚手架安装教程)

    vue-cli2.x旧版本卸载不掉的问题踩坑指南(附Vue脚手架安装教程)

    遇到一个Vuecli2脚手架卸载不了的问题,查了许多资料说的都比较复杂,所以下面这篇文章主要给大家介绍了关于vue-cli2.x旧版本卸载不掉的问题踩坑的相关资料,文中还附了Vue脚手架安装教程,需要的朋友可以参考下
    2022-07-07
  • 详细解释Vue3中的getCurrentInstance是什么

    详细解释Vue3中的getCurrentInstance是什么

    这篇文章主要介绍了Vue3中getCurrentInstance是什么的相关资料,getCurrentInstance是Vue3提供的一个API,用于在组合式API中获取当前组件实例,从而访问组件的属性、方法、插槽等信息,文中将用法介绍的非常详细,需要的朋友可以参考下
    2025-05-05
  • elementPlus 的el-select在提示框关闭时自动弹出的问题解决

    elementPlus 的el-select在提示框关闭时自动弹出的问题解决

    这篇文章主要介绍了elementPlus 的el-select在提示框关闭时自动弹出闭时自动弹出的问题,主要问题就是因为filterable属性,根本解决方案是选中的时候让他失去焦点 el-select有一个visible-change事件,下拉框出现/隐藏时触发,感兴趣的朋友跟随小编一起看看吧
    2024-01-01
  • Element input树型下拉框的实现代码

    Element input树型下拉框的实现代码

    这篇文章主要介绍了Element input树型下拉框的实现代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-12-12
  • moment转化时间戳出现Invalid Date的问题及解决

    moment转化时间戳出现Invalid Date的问题及解决

    这篇文章主要介绍了moment转化时间戳出现Invalid Date的问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-05
  • vue实现div拖拽互换位置

    vue实现div拖拽互换位置

    这篇文章主要为大家详细介绍了vue实现div拖拽互换位置的方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-12-12
  • 教你如何优化 Vue.js 应用程序

    教你如何优化 Vue.js 应用程序

    Vue 3引入了Composition API,这是一套新的API用于编写组件,作为Options API的替代,这篇文章主要介绍了如何优化 Vue.js 应用程序,需要的朋友可以参考下
    2023-02-02

最新评论