虚拟滚动解决前端大规模数据渲染问题

场景:需要全量展示列表数据,但数据量极大,会导致页面超级卡顿。通常选择虚拟滚动而不用分页的原因是为了方便某些操作(比如全选)。

解决方案:根据组件的可视范围,只渲染页面可见部分所需要的dom元素,而不是全量渲染。

具体操作步骤:

1、通过接口,拿到列表的全量数据this.dataList。(数据量大该过程可能会较慢,但我们前端目前只解决大规模渲染的卡顿问题,接口慢先不做考虑)

2、通过组件视口的高度和列表项视口的高度,决定每次渲染的数据条数。

computed: {
        // 这个是截取表格中的部分数据,放到了 table 组件中来显示
        sliceTable() {
            return this.dataList.slice(
                this.startIndex, ((this.dataList.length - this.startIndex) > 9) ? (this.startIndex + 9) : this.dataList.length);
        }
    },

以上代码是每次渲染9条,并考虑到了边界情况。

3、设置样式,出现滚动条模拟。

计算列表(.el-table__body)中所有数据应占得高度height,创建一个div并设置高度为height用于撑开容器(.el-table__body-wrapper),出现滚动条,暂且称这个div为‘假盒子’(this.vEle)。

注意:列表(.el-table__body)元素要设置绝对定位,以使得列表悬浮在‘假盒子’上方。

// 设置成绝对定位,这个元素需要我们去控制滚动
                this.$refs.tableRef.$el.querySelector(".el-table__body").style.position = "absolute";
                // 计算表格所有数据所占内容的高度
                this.vEle.style.height = this.dataList.length * 44 + "px";
                // 把这个节点加到表格中去,用它来撑开表格的高度
                this.$refs.tableRef.$el.querySelector(".el-table__body-wrapper").style.overflowY = 'auto'
                this.$refs.tableRef.$el.querySelector(".el-table__body-wrapper").appendChild(this.vEle);

容器的overflow-y属性设置为auto;

4、给容器(.el-table__body-wrapper)添加滚动事件,更新this.startIndex,从而更新需要渲染的列表数据this.sliceTable。注意:添加滚动事件会使列表(.el-table__body)和‘假盒子’(this.vEle)一起滚动,而我们要保持列表不动,更新渲染数据,造成列表滚动的假象,就要逆向移动列表(.el-table__body)。

let bodyWrapperEle = this.$refs.tableRef.$el.querySelector(".el-table__body-wrapper");
            // 滚动的高度
            let scrollTop = bodyWrapperEle.scrollTop;
            // 下一次开始的索引
            this.startIndex = Math.floor(scrollTop / 44);
            // 滚动操作
            bodyWrapperEle.querySelector(".el-table__body").style.transform = `translateY(${this.startIndex * 44}px)`;
### 前端数据量初始化渲染的最佳实践 在前端开发中,当涉及到大量数据的初始渲染时,性能优化显得尤为重要。以下是几种常见的最佳实践和方法: #### 数据分片加载 通过将大规模数据集分割成较小的部分逐步加载到页面上,可以有效减少单次 DOM 操作的压力。这种方法通常被称为“数据分页”或“虚拟滚动”。它能够显著提升用户体验并降低内存占用[^1]。 ```javascript function renderDataChunk(data, chunkSize) { let index = 0; const totalChunks = Math.ceil(data.length / chunkSize); function loadNextChunk() { if (index >= data.length) return; const end = Math.min(index + chunkSize, data.length); const currentChunk = data.slice(index, end); // 渲染当前片段至DOM updateUI(currentChunk); index += chunkSize; if (index < data.length){ requestAnimationFrame(loadNextChunk); } } loadNextChunk(); } ``` 此函数利用 `requestAnimationFrame` 来逐块加载数据,从而避免阻塞主线程[^2]。 #### 虚拟列表技术 对于超长列表或者表格展示场景,“虚拟列表”是一种非常有效的解决方案。其核心思想在于只渲染视口内的可见项,并动态调整随着用户的滚动行为而更新显示的内容。这不仅减少了不必要的 DOM 元素创建开销,还提高了整体效率。 ```html <div id="virtual-list" style="height: 300px; overflow-y: auto;"></div> ``` ```javascript class VirtualList { constructor(containerId, items, itemHeight) { this.container = document.getElementById(containerId); this.items = items; this.itemHeight = itemHeight; this.scrollTop = 0; this.visibleCount = Math.ceil(this.container.clientHeight / this.itemHeight); this.renderedItems = []; this._initScrollEvent(); } _renderVisibleRange(startIndex, endIndex) { for(let i=startIndex;i<=endIndex && i<this.items.length;i++) { const el = document.createElement('div'); el.textContent = 'Item '+i; el.style.height = `${this.itemHeight}px`; if(!this.renderedItems[i]){ this.container.appendChild(el); this.renderedItems.push(el); }else{ this.container.insertBefore(this.renderedItems[i], null); } } } _clearNonVisibleElements(startIndex, endIndex){ this.renderedItems.forEach((el,idx)=>{ if(idx<startIndex || idx>endIndex){ this.container.removeChild(el); } }); } _onScroll(){ const scrollTop = this.container.scrollTop; const startIndex = Math.floor(scrollTop / this.itemHeight); const endIndex = startIndex + this.visibleCount -1 ; this._renderVisibleRange(startIndex,endIndex); this._clearNonVisibleElements(startIndex,endIndex); } _initScrollEvent(){ this.container.addEventListener('scroll', ()=>{ this._onScroll(); }, false ); } } new VirtualList('virtual-list', Array.from({length:1e6}, (_,i)=>i), 50); ``` 上述代码展示了如何实现一个简单的虚拟列表组件来管理大量的条目。 #### 使用 Web Workers 处理复杂计算 如果某些逻辑运算特别耗时,则可考虑将其移交给后台线程——即Web Worker执行。这样做的好处是可以让 UI 线程保持响应状态而不被长时间占据。 ```javascript // main.js const worker = new Worker('./worker.js'); worker.postMessage(largeDataset); worker.onmessage = function(e){ console.log("Received from worker:", e.data); }; // worker.js self.onmessage = function(event){ const processedData = processData(event.data); self.postMessage(processedData); }; ``` 以上例子说明了怎样借助Worker来进行异步的大规模数据预处理工作。 #### 性能监控与调试工具的应用 最后,在实施任何一种优化策略之后都需要对其进行评估验证。可以通过浏览器内置开发者工具查看帧率(FPS),JavaScript CPU时间分布图谱以及网络请求状况等指标来判断改进措施的效果。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值