当前位置: 首页 > news >正文

前端面试手写--虚拟列表

目录

一.问题背景

二.代码讲解

三.代码改装

四.代码发布


今天我们来学习如何手写一个虚拟列表,本文将把虚拟列表进行拆分并讲解,然后发布到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

欢迎下载尝试喵!

相关文章:

前端面试手写--虚拟列表

目录 一.问题背景 二.代码讲解 三.代码改装 四.代码发布 今天我们来学习如何手写一个虚拟列表,本文将把虚拟列表进行拆分并讲解,然后发布到npm网站上. 一.问题背景 为什么需要虚拟列表呢?这是因为在面对大量数据的时候,我们的浏览器会将所有数据都渲染到表格上面,但是渲…...

达梦数据库针对慢SQL,收集统计信息清除执行计划缓存

前言&#xff1a;若遇到以下场景&#xff0c;大概率是SQL走错了执行计划&#xff1a; 1、一条SQL在页面上查询特别慢&#xff0c;但拿到数据库终端执行特别快 2、一条SQL在某种检索条件下查询特别慢&#xff0c;但拿到数据库终端执行特别快 此时&#xff0c;可以尝试按照下述步…...

李沐--动手学深度学习 序列模型

1.使用正弦函数和可加性噪声生成序列数据 import torch from torch import nn from d2l import torch as d2l#使用正弦函数和可加性噪声生成序列数据 T 1000 #总共产生1000个点 time torch.arange(1,T1,dtypetorch.float32) x torch.sin(0.01*time) torch.normal(0,0.2,(…...

数据分析、商业智能、业务分析三者之间的关系

商业智能 (Business Intelligence, BI)、业务分析 (Business Analytics, BA) 和数据分析 (Data Analytics, DA) 三者都与数据密切相关&#xff0c;但在目标、方法和应用上存在差异。为了能够清晰地解释&#xff0c;下面将从定义入手&#xff0c;然后阐述它们之间的联系和区别。…...

【Spring+MyBatis】留言墙的实现

目录 1. 添加依赖 2. 配置数据库 2.1 创建数据库与数据表 2.2 创建与数据库对应的实体类 3. 后端代码 3.1 目录结构 3.2 MessageController类 3.3 MessageService类 3.4 MessageMapper接口 4. 前端代码 5. 单元测试 5.1 后端接口测试 5.2 使用前端页面测试 在Spri…...

让编程变成一种享受-明基RD320U显示器

引言 作为一名有着多年JAVA开发经验的从业者&#xff0c;在工作过程中&#xff0c;显示器的重要性不言而喻。它不仅是我们与代码交互的窗口&#xff0c;更是影响工作效率和体验的关键因素。在多年的编程生涯中&#xff0c;我遇到过各种各样的问题。比如&#xff0c;在进行代码…...

【嵌入式Linux应用开发基础】fork()函数

目录 一、fork 函数概述 1.1. 函数作用 1.2. 函数原型与头文件 1.3. 返回值 1.4. 核心特性 二、父子进程的区别与联系 2.1. 相同点 2.2. 不同点 三、典型应用场景 3.1. 多任务处理 3.2. 守护进程创建 3.3. 执行外部程序 3.4. 并行计算 四、fork 函数的关键注意事…...

2024 年 CSDN 博客之星年度评选:技术创作与影响力的碰撞(统计时间2025-02-17 11:06:06)

摘要&#xff1a;在技术的海洋里&#xff0c;每一位博主都像是一座独特的灯塔&#xff0c;用自己创作的光芒照亮他人前行的道路。2024 年 CSDN 博客之星年度评选活动&#xff0c;正是对这些灯塔的一次盛大检阅&#xff0c;让我们看到了众多优秀博主在技术创作领域的卓越表现以及…...

串的基本操作--数据结构

目录 一、串的基本概述 二、串的存储结构 2.1定义属性存储结构 串长有两种表示方法: 1、用一个额外的变量length来存放串的长度&#xff1b; 2、串值后面加一个不计入串长的结束标记字符“\0”&#xff0c;此时的串长为隐含值。 2.2堆的顺序存储结构 三、串的基本操…...

Unity 命令行设置运行在指定的显卡上

设置运行在指定的显卡上 -force-device-index...

Dest1ny漏洞库: 美团代付微信小程序系统任意文件读取漏洞

大家好&#xff0c;今天是Dest1ny漏洞库的专题&#xff01;&#xff01; 会时不时发送新的漏洞资讯&#xff01;&#xff01; 大家多多关注&#xff0c;多多点赞&#xff01;&#xff01;&#xff01; 0x01 产品简介 美团代付微信小程序系统是美团点评旗下的一款基于微信小程…...

设计模式:状态模式

状态机有3个要素&#xff1a;状态&#xff0c;事件&#xff0c;动作。 假如一个对象有3个状态:S1、S2、S3。影响状态的事件有3个&#xff1a;E1、E2、E3。每个状态下收到对应事件的时候&#xff0c;对象的动作为AXY。那么该对象的状态机就可以用如下表格来表示。S1收到事件E1的…...

【故障处理】- 执行命令crsctl query crs xxx一直hang

【故障处理】- 执行命令crsctl query crs xxx一直hang 一、概述二、故障处理三、解决方法 一、概述 Oracle RAC环境中&#xff0c;遇到执行crsctl query crs xxx等相关命令不返回任何结果&#xff0c;一直hang在那里。系统下执行命令ps -ef |grep crsctl query crs softwarever…...

Zabbix——监控Nginx

背景 在项目中使用Nginx之后&#xff0c;有时候我们需要知道Nginx具体的工作情况&#xff0c;这时候就需要使用zabbix进行Nginx的相关监控 这边我们有两种方法 使用普通的http请求的方式获取基本信息如果使用了Nginx Plus&#xff0c;就可以通过Nginx Plus的接口获取更多的信…...

开源工具推荐--思维导图、流程图等绘制

1. 前言 在工作中&#xff0c;经常要用到各种不同的工具&#xff0c;随着系统的升级&#xff0c;有些工具也在不断更新升级。这里收集整理一些好用的开源工具推荐&#xff0c;遵循以下一些基本原则&#xff1a;开源免费&#xff0c;商业工具的有效平替&#xff0c;轻量级&…...

【论文笔记】Transformer^2: 自适应大型语言模型

Code repo: https://github.com/SakanaAI/self-adaptive-llms 摘要 自适应大型语言模型&#xff08;LLMs&#xff09;旨在解决传统微调方法的挑战&#xff0c;这些方法通常计算密集且难以处理多样化的任务。本文介绍了Transformer&#xff08;Transformer-Squared&#xff09;…...

FFmpeg源码:av_strlcpy函数分析

一、引言 在C/C编程中经常会用到strcpy这个字符串复制函数。strcpy是C/C中的一个标准函数&#xff0c;可以把含有\0结束符的字符串复制到另一个地址空间。但是strcpy不会检查目标数组dst的大小是否足以容纳源字符串src&#xff0c;如果目标数组太小&#xff0c;将会导致缓冲区…...

Unity Shader学习6:多盏平行光+点光源 ( 逐像素 ) 前向渲染 (Built-In)

0 、分析 在前向渲染中&#xff0c;对于逐像素光源来说&#xff0c;①ForwardBase中只计算一个平行光&#xff0c;其他的光都是在FowardAdd中计算的&#xff0c;所以为了能够渲染出其他的光照&#xff0c;需要在第二个Pass中再来一遍光照计算。 而有所区别的操作是&#xff0…...

docker批量pull/save/load/tag/push镜像shell脚本

目录 注意&#xff1a; 脚本内容 执行效果 注意&#xff1a; 以下脚本为shell脚本通过docker/nerdctl进行镜像独立打包镜像的相关操作脚本内仓库信息和镜像存取路径需自行更改需自行创建images.txt并填写值&#xff0c;并且与脚本位于同级目录下 [rootmaster01 sulibao]# l…...

五十天精通硬件设计第32天-S参数

系列文章传送门 50天精通硬件设计第一天-总体规划-CSDN博客 目录 1. S参数基础 2. S参数在信号完整性中的作用 3. 单端 vs. 差分S参数 4. S参数的关键特性 5. S参数的获取与使用 6. S参数分析中的常见问题 7. 实际案例:PCIe通道分析 8. 工具推荐 总结 信号完整性中…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

Flask RESTful 示例

目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题&#xff1a; 下面创建一个简单的Flask RESTful API示例。首先&#xff0c;我们需要创建环境&#xff0c;安装必要的依赖&#xff0c;然后…...

Spark 之 入门讲解详细版(1)

1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室&#xff08;Algorithms, Machines, and People Lab&#xff09;开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目&#xff0c;8个月后成为Apache顶级项目&#xff0c;速度之快足见过人之处&…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销&#xff0c;平衡网络负载&#xff0c;延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练

前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1)&#xff1a;从基础到实战的深度解析-CSDN博客&#xff0c;但实际面试中&#xff0c;企业更关注候选人对复杂场景的应对能力&#xff08;如多设备并发扫描、低功耗与高发现率的平衡&#xff09;和前沿技术的…...

在四层代理中还原真实客户端ngx_stream_realip_module

一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡&#xff08;如 HAProxy、AWS NLB、阿里 SLB&#xff09;发起上游连接时&#xff0c;将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后&#xff0c;ngx_stream_realip_module 从中提取原始信息…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库&#xff0c;专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性&#xff0c;并提供了一个通用的框架&…...

Fabric V2.5 通用溯源系统——增加图片上传与下载功能

fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...