应用场景
前端的业务开发中会遇到不使用分页方式来加载长列表的需求。如在数据长度大于 1000 条情况,DOM 元素的创建和渲染需要的时间成本很高,完整渲染列表所需要的时间不可接受,同时会存在滚动时卡顿问题;
解决该卡顿问题的重点在于如何降低长列表DOM渲染成本问题,文章将介绍通过虚拟列表渲染的方式解决该问题。
虚拟列表的核心思想为可视区域渲染,在页面滚动时对数据进行截取、复用DOM进行展示的渲染方式。
实现虚拟列表就是处理滚动条滚动后的可见区域的变更,其中具体步骤如下:
我们首先要考虑的是虚拟列表的 HTML、CSS 如何实现:
<template> <div class="list-view" :style="{ height: `${height}px` }" @scroll="handleScroll"> <div class="list-view-phantom" :style="{ height: contentHeight }"> </div> <ul ref="content" class="list-view-content"> <li class="list-view-item" :style="{ height: itemHeight + ‘px‘ }" v-for="(item, index) in visibleData" :key="index"> {{ item }} </li> </ul> </div> </template> <script> export default { name: ‘ListView‘, props: { data: { type: Array, default: function() { const list = [] for (let i = 0; i < 1000000; i++) { list.push(‘列表‘ + i) } return list } }, height: { type: Number, default: 400 }, itemHeight: { type: Number, default: 30 }, }, computed: { contentHeight() { return this.data.length * this.itemHeight + ‘px‘; } }, mounted() { this.updateVisibleData(); }, data() { return { visibleData: [] }; }, methods: { updateVisibleData(scrollTop) { scrollTop = scrollTop || 0; const visibleCount = Math.ceil(this.$el.clientHeight / this.itemHeight); const start = Math.floor(scrollTop / this.itemHeight); const end = start + visibleCount; this.visibleData = this.data.slice(start, end); this.$refs.content.style.webkitTransform = `translate3d(0, ${ start * this.itemHeight }px, 0)`; }, updateVisibleData(scrollTop) { scrollTop = scrollTop || 0; const visibleCount = Math.ceil(this.$el.clientHeight / this.itemHeight); // 取得可见区域的可见列表项数量 const start = Math.floor(scrollTop / this.itemHeight); // 取得可见区域的起始数据索引 const end = start + visibleCount; // 取得可见区域的结束数据索引 this.visibleData = this.data.slice(start, end); // 计算出可见区域对应的数据,让 Vue.js 更新 this.$refs.content.style.webkitTransform = `translate3d(0, ${ start * this.itemHeight }px, 0)`; // 把可见区域的 top 设置为起始元素在整个列表中的位置(使用 transform 是为了更好的性能) }, handleScroll() { const scrollTop = this.$el.scrollTop; this.updateVisibleData(scrollTop); } } } </script> <style lang="scss" scoped> .list-view { overflow: auto; position: relative; border: 1px solid #aaa; width: 200px; } .list-view-phantom { position: absolute; left: 0; top: 0; right: 0; z-index: -1; } .list-view-content { left: 0; right: 0; top: 0; position: absolute; } .list-view-item { padding: 5px; color: #666; line-height: 30px; box-sizing: border-box; } </style>
原文:https://www.cnblogs.com/luckknock/p/14736542.html