前端面試手寫--虛擬列表

目錄

一.問題背景

二.代碼講解

三.代碼改裝

四.代碼發布


今天我們來學習如何手寫一個虛擬列表,本文將把虛擬列表進行拆分并講解,然后發布到npm網站上.

一.問題背景

為什么需要虛擬列表呢?這是因為在面對大量數據的時候,我們的瀏覽器會將所有數據都渲染到表格上面,但是渲染極其消耗時間,就會出現瀏覽器卡頓的現象.總的來說就是機器性能不行,需要前端對體驗進行優化.

同時呢,我們其實正常人眼睛能看清的程度下,一個屏幕也就20-50行數據,面對10^4以上的數據的時候,如果為了只看這么點數據,而將所有數據都直接渲染,這會在短時間消耗大量的算力.

我們前端對同樣數據量的數據獲取和數據渲染是兩個過程,其中渲染的速度遠慢于數據獲取.所以我們采用虛擬列表這個技巧,每次計算視口可以容納幾個元素,讓后將這些元素從總列表當中計算出來,只渲染這部分可視數據,就將壓力分散到各段時間,很大程度上可以降低性能壓力.

二.代碼講解

我最開始看的面經,作者是用的Vue2寫的(不過我忘記出處了),代碼如下:

 <!-- /component/HelloWorld.vue --><template><div ref="list" class="infinite-list-container" @scroll="scrollEvent($event)"><divclass="infinite-list-phantom":style="{ height: listHeight + 'px' }"></div><div class="infinite-list" :style="{ transform: getTransform }"><divref="items"class="infinite-list-item"v-for="item in visibleData":key="item.id":style="{ height: itemSize + 'px', lineHeight: itemSize + 'px' }">{{ item.value }}</div></div></div>
</template><script>
export default {name: "TheVirtualList",props: {//所有列表數據listData: {type: Array,default: () => [],},//每項高度itemSize: {type: Number,default: 200,},},computed: {//列表總高度listHeight() {return this.listData.length * this.itemSize;},//可顯示的列表項數visibleCount() {return Math.ceil(this.screenHeight / this.itemSize);},//偏移量對應的stylegetTransform() {return `translate3d(0,${this.startOffset}px,0)`;},//獲取真實顯示列表數據visibleData() {return this.listData.slice(this.start,Math.min(this.end, this.listData.length));},},mounted() {this.screenHeight = this.$el.clientHeight;console.log('查看高度',this.screenHeight);this.start = 0;this.end = this.start + this.visibleCount;// console.log(`查看傳入組件參數:,${this.itemSize}`);},data() {return {//可視區域高度screenHeight: 0,//偏移量startOffset: 0,//起始索引start: 0,//結束索引end: null,};},methods: {scrollEvent() {//當前滾動位置let scrollTop = this.$refs.list.scrollTop;//此時的開始索引this.start = Math.floor(scrollTop / this.itemSize);//此時的結束索引this.end = this.start + this.visibleCount;//此時的偏移量this.startOffset = scrollTop - (scrollTop % this.itemSize);console.log('查看滾動位置:',scrollTop);},},
};
</script><style scoped>
.infinite-list-container {height: 100%;overflow: auto;position: relative;-webkit-overflow-scrolling: touch;
}.infinite-list-phantom {position: absolute;left: 0;top: 0;right: 0;/* z-index: -1; */
}.infinite-list {left: 0;right: 0;top: 0;/* position: absolute; */text-align: center;
}.infinite-list-item {padding: 10px;color: #555;box-sizing: border-box;border-bottom: 1px solid #999;
}
</style>

這是一段Vue2代碼,我來解釋下其中的特殊之處

this.$el.clientHeight;

這段代碼,代表獲取當前組件的高度

this.$refs.list.scrollTop;

這串代碼,代表獲取ref值為list的容器的右側滑動條距離頂部有多少px

這里面用到的技巧就是,這個虛擬表格看起來是一個高度很高的容器,包裹著一個填滿了數據的子容器,滑不完

實際上該組件是一個高度很高的空容器+一個只顯示一個屏幕數據量的容器+絕對定位進行布局

實際效果如下:

三.代碼改裝

由于現在都在寫Vue,切是組合式,雖然框架兼容vue2寫法,但是還是習慣Vue3組合式的寫法

不同的是,因為我們不能再用this.$el.clientHeight;來獲取該組件的高度了,所以我在組件最外層又套了一個div,并打了一個ref,用其的高度來進行代替.

代碼如下:

<template><div ref="virtualList" style="height: 100%;"><div ref="list" class="infinite-list-container" @scroll="handleScroll()"><divclass="infinite-list-phantom":style="{ height:  `${listHeight}px` }"></div><div class="infinite-list" :style="{ transform: getTransform }"><divref="items"class="infinite-list-item"v-for="item in visibleData":key="item.id":style="{ height:  `${itemSize}px`, lineHeight: `${itemSize}px` }">{{ item.value }}</div></div></div></div>
</template><script setup>
import { onMounted } from 'vue';
import { computed } from 'vue';
import { defineProps ,ref} from 'vue';const props = defineProps({//所有列表數據listData: {type: Array,default: () => [],},//每項高度itemSize: {type: Number,default: 200,},
})
const virtualList = ref(null);
const list = ref(null)
// 虛擬列表的數據結構
const virtualListInfo = ref({start: 0,end: null,startOffset: 0,
})
// 虛擬底板高度
const listHeight = computed(() => {return props.listData.length * props.itemSize
})
// 獲取偏移
const getTransform = computed(() => {return `translate3d(0,${virtualListInfo.value.startOffset}px,0)`;
})
// 一頁渲染的元素個數
const visibleCount = computed(() => {return Math.ceil(virtualListInfo.value.screenHeight / props.itemSize);
})
// 要渲染的列表
const visibleData = computed(() => {// slice特性,如果第二個參數超過數組長度,那么直接獲取到末尾即可return props.listData.slice(virtualListInfo.value.start,virtualListInfo.value.end)
})
const aaa = ref(1)
console.log('xxxxx',props,aaa);const handleScroll = ()=> {if (list.value) {let scrollTop = list.value.scrollTop//此時的開始索引virtualListInfo.value.start = Math.floor(scrollTop / props.itemSize);//此時的結束索引virtualListInfo.value.end = virtualListInfo.value.start + visibleCount.value;//此時的偏移量virtualListInfo.value.startOffset = scrollTop - (scrollTop % props.itemSize);console.log('查看滾動位置:',scrollTop);} else {console.log('list未引用');}
}
onMounted(() => {virtualListInfo.value.screenHeight = virtualList.value.clientHeight;console.log('查看高度',virtualListInfo.value.screenHeight);virtualListInfo.value.start = 0;virtualListInfo.value.end = virtualListInfo.value.start + visibleCount.value;// console.log(`查看傳入組件參數:,${this.itemSize}`);
})
</script><style scoped>
.infinite-list-container {height: 100%;overflow: auto;position: relative;-webkit-overflow-scrolling: touch;
}.infinite-list-phantom {position: absolute;left: 0;top: 0;right: 0;/* z-index: -1; */
}.infinite-list {left: 0;right: 0;top: 0;/* position: absolute; */text-align: center;
}.infinite-list-item {padding: 10px;color: #555;box-sizing: border-box;border-bottom: 1px solid #999;
}
</style>

四.代碼發布

我們封裝好了這些組件,但是想要其他人發布,所以我們就需要將其放到公網上.像我們開發項目需要從倉庫下載別人的包一樣,我們也可以發布自己的包到npm倉庫上

你可以參考:如何發布自己的npm包(超詳細步驟,博主都在用)_npm 發布-CSDN博客

我將上面的代碼發布到我自己的倉庫了,名稱為:lqd-raw-component

歡迎下載嘗試喵!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/895683.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/895683.shtml
英文地址,請注明出處:http://en.pswp.cn/news/895683.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

vue項目本地svg圖標使用

提前準備&#xff1a; 1、一個本地的svg圖片 這個直接從網上找一個就行 2、文件整體目錄 安裝插件 npm i vite-plugin-svg-iconsvite.config.ts中配置插件 完整代碼 import { fileURLToPath, URL } from node:url import { resolve } from path import { defineConfig } f…

Go: 使用VS Code配置Go項目支持Windows與Linux雙系統調試

在現代軟件開發中&#xff0c;越來越多的開發者開始使用VS Code等集成開發環境&#xff08;IDE&#xff09;來提高生產力&#xff0c;特別是在支持遠程開發時。VS Code的遠程SSH功能&#xff0c;使得開發者可以在本地Windows電腦上&#xff0c;通過遠程SSH連接到Linux服務器&am…

萌新學 Python 之集合 set

集合 set&#xff1a;使用一對大括號&#xff0c;元素寫在大括號之間&#xff0c;使用逗號分隔 集合中的元素只能是不可變的數據類型&#xff0c;不能是列表、字典和集合 set1 {1, 2, 3} set2 {1, a, (1, 2, 3)} print(type(set1), type(set2)) # <class set> <c…

python中使用數據庫sqlite3

Python使用sqlite3數據庫 python3.x標準庫內置了SQLite3 查看sqlite的版本 import sqlite3 sqlite_version sqlite3.sqlite_version print(f"SQLite version: {sqlite_version}") 顯示 導入模塊連接sqlitte3 import sqlite3 consqlite3.connect("d:/fi…

maven使用默認settings.xml配置時,Idea基于pom.xml更新依賴時報錯,有些組件下載時連接超時

1、問題背景&#xff1a;maven使用默認settings.xml配置時&#xff0c;Idea基于pom.xml更新依賴時報錯&#xff0c;有些組件下載時連接超時&#xff0c; 通過日志發下&#xff0c;去連接maven.org網站下載依賴&#xff0c;有時候肯定會超時。 2、解決辦法&#xff1a;使用國外…

小狐貍ai3.1.2版本源碼無授權版本內 含搭建教程+各種上線教程

內容目錄 一、詳細介紹小狐貍3.1.2版本源碼&#xff0c;新增deepseek接口 文件夾說明&#xff1a; 1、后端&#xff1a;文件夾是后臺文件 5、.sql文件是數據庫文件后臺安裝步驟&#xff1a; 1、在寶塔新建個站點&#xff0c;php版本使用7.4&#xff0c;將“后端”文件夾里的文件…

C#之上位機開發---------C#通信庫及WPF的簡單實踐

〇、上位機&#xff0c;分層架構 界面層 要實現的功能&#xff1a; 展示數據 獲取數據 發送數據 數據層 要實現的功能&#xff1a; 轉換數據 打包數據 存取數據 通信層 要實現的功能&#xff1a; 打開連接 關閉連接 讀取數據 寫入數據 實體類 作用&#xff1a; 封裝數據…

Python Django 入門教程

Django 構建一個完整的博客平臺,包含用戶認證、評論、權限控制等功能。 環境搭建 安裝依賴 先確保已經安裝了 Python 環境,并通過 pip 安裝 Django 及其它依賴: pip install django pip install djangorestframework創建項目和應用 創建一個 Django 項目并進入該項目目錄:…

今日行情明日機會——20250217

2025年02月17日行情 后續投資機會分析 根據最新盤面信息&#xff0c;以下板塊和個股具備潛在投資機會&#xff0c;需結合市場動態和基本面進一步驗證&#xff1a; 1. 騰訊系AI&#xff08;18家漲停&#xff09; 核心邏輯&#xff1a;漲停家數最多&#xff08;18家&#xff0…

常見的IP地址分配方式有幾種:深入剖析與適用場景?

在數字互聯的世界里&#xff0c;IP地址如同網絡世界的“門牌號”&#xff0c;是設備間通信的基礎。隨著網絡技術的飛速發展&#xff0c;IP地址的分配方式也日趨多樣化&#xff0c;以適應不同規模、不同需求的網絡環境。本文將深入探討當前主流的幾種IP地址分配方式&#xff0c;…

快速搭建 OLLAMA + DeepSeek 模型并對接 Cherry Studio

快速搭建 OLLAMA DeepSeek 模型并對接 Cherry Studio 本文將指導您在基于 GPU 的 Ubuntu 服務器上快速搭建 OLLAMA&#xff0c;部署 DeepSeek 模型&#xff0c;并接入 Cherry Studio 進行使用。 環境說明 GPU 服務器: GeForce RTX 2080 Ti, 16 核, 64G 內存系統: Ubuntu 24.…

floodfill算法系列一>掃雷游戲

目錄 題目思路&#xff1a;代碼設計&#xff1a;代碼呈現&#xff1a; 題目思路&#xff1a; 代碼設計&#xff1a; 代碼呈現&#xff1a; class Solution {int m,n;int[] dx {0,0,-1,1,-1,-1,1,1};int[] dy {-1,1,0,0,-1,1,-1,1};public char[][] updateBoard(char[][] boa…

Java 中操作文件

文章目錄 Java 中操作文件File 概述屬性構造方法方法代碼示例文件內容的讀寫--數據流InputStream 概述常用方法FileInputStream 概述構造方法常用方法代碼示例通過 Scanner 進行字符讀取FileReaderOutputStream 概述常用方法FileOutputStream 概述構造方法常用方法代碼示例File…

什么是RDD以及它在Spark中的作用

RDD&#xff08;Resilient Distributed Dataset&#xff09;&#xff0c;即彈性分布式數據集&#xff0c;是Spark中最基本的數據抽象。以下是對RDD的詳細解釋以及它在Spark中的作用&#xff1a; 一、RDD的定義與特性 定義&#xff1a; RDD是一個不可變、可分區、里面的元素可并…

【附帶腳本】解決notion加載慢問題

問題原因 notion網站的服務器在國外&#xff0c;因為網絡問題&#xff08;國際出口帶寬限制&#xff09;導致訪問速度較慢和域名解析延遲等問題。 解決方案 通過在 hosts 文件中直接指定一個更快的 IP 地址&#xff08;例如國內鏡像服務器&#xff09;&#xff0c;可以顯著提…

Banana Pi OpenWRT One 官方路由器的第一印象

OpenWRT One是OpenWRT開源社區推出的首款官方開發板&#xff0c;與Banana Pi社區共同設計&#xff0c;由Banana Pi制造和發行。路由器采用藍色鋁合金外殼&#xff0c;質感極佳&#xff0c;視覺效果遠超宣傳圖。整體設計簡潔&#xff0c;呈長方形&#xff0c;雖然不是特別時尚&a…

【信息學奧賽一本通 C++題解】1285:最大上升子序列和

信息學奧賽一本通&#xff08;C版&#xff09;在線評測系統 基礎算法 第一節 動態規劃的基本模型 1285&#xff1a;最大上升子序列和 “最大上升子序列和”問題課堂講解 1. 理解題意 同學們&#xff0c;想象我們有一串數字&#xff0c;就像一串彩色的珠子&#xff0c;每個珠子…

刷題記錄Day4(補)

24. 兩兩交換鏈表中的節點 ① 使用虛擬節點 ② 最后返回頭結點的時候&#xff0c;head 本來的頭節點已經和第二位交換了&#xff0c;需要重新賦值 ③ 使用臨時指針保存變量 ④ 如果是空的不用特殊判斷&#xff0c;空的返回頭節點也還是空的 class Solution { public:ListNo…

花西子攜手賽博威共創新品創新平臺,驅動“新質美力”高質量發展

國貨彩妝品牌花西子與賽博威信息科技達成【新品創新平臺】項目合作&#xff0c;共探“新質美力”的高質量發展路徑。 近日&#xff0c;賽博威信息科技CEO陳國平攜團隊走進花西子“百年之詩”館&#xff0c;深入了解花西子的品牌理念、企業文化及百年愿景&#xff0c;并與花西子…

[JVM篇]垃圾回收器

垃圾回收器 Serial Seral Old PartNew CMS(Concurrent Mark Sweep) Parallel Scavenge Parallel Old G1 ZGC