vue svelte solid 虚拟滚动性能对比
前言
由于svelte solid 两大无虚拟DOM框架,由于其性能好,在前端越来越有影响力。
因此本次想要验证,这三个框架关于实现表格虚拟滚动的性能。
比较版本
- vue@3.4.21
- svelte@4.2.12
- solid-js@1.8.15
比较代码
这里使用了我的 stk-table-vue(npm) 中实现虚拟滚动主要代码。
StkTable.vue
<script setup>
import { onMounted, ref, computed } from 'vue';
const props = defineProps({virtual: {type: Boolean,default: true},columns: {type: Array},dataSource: {type: Array}
})
const tableContainer = ref();
const virtualScroll = ref({containerHeight: 0,startIndex: 0, // 数组开始位置rowHeight: 28,offsetTop: 0, // 表格定位上边距scrollTop: 0, // 纵向滚动条位置,用于判断是横向滚动还是纵向
});const dataSourceCopy = computed(() => [...props.dataSource]);const virtual_pageSize = computed(() => Math.ceil(virtualScroll.value.containerHeight / virtualScroll.value.rowHeight) + 1); // 这里最终+1,因为headless=true无头时,需要上下各预渲染一行。const virtual_on = computed(() => props.virtual && dataSourceCopy.value.length > virtual_pageSize.value * 2)
const virtual_dataSourcePart = computed(() => virtual_on.value? dataSourceCopy.value.slice(virtualScroll.value.startIndex, virtualScroll.value.startIndex + virtual_pageSize.value): dataSourceCopy.value)
const virtual_offsetBottom = computed(() => virtual_on.value ? (dataSourceCopy.value.length - virtualScroll.value.startIndex - virtual_dataSourcePart.value.length) * virtualScroll.value.rowHeight : 0)onMounted(() => {initVirtualScroll();
})/*** 初始化虚拟滚动参数* @param {number} [height] 虚拟滚动的高度*/
function initVirtualScroll(height) {initVirtualScrollY(height);// this.initVirtualScrollX();
}
/*** 初始化Y虚拟滚动参数* @param {number} [height] 虚拟滚动的高度*/
function initVirtualScrollY(height) {if (virtual_on.value) {virtualScroll.value.containerHeight = typeof height === 'number' ? height : tableContainer.value?.offsetHeight;updateVirtualScrollY(tableContainer.value?.scrollTop);}
}
/** 通过滚动条位置,计算虚拟滚动的参数 */
function updateVirtualScrollY(sTop = 0) {const { rowHeight } = virtualScroll.value;const startIndex = Math.floor(sTop / rowHeight);Object.assign(virtualScroll.value, {startIndex,offsetTop: startIndex * rowHeight, // startIndex之前的高度});
}function onTableScroll(e) {if (!e?.target) return;// 此处可优化,因为访问e.target.scrollXX消耗性能const { scrollTop, scrollLeft } = e.target;// 纵向滚动有变化if (scrollTop !== virtualScroll.value.scrollTop) virtualScroll.value.scrollTop = scrollTop;if (virtual_on.value) {updateVirtualScrollY(scrollTop);}
}
</script>
<template><div class="stk-table" ref="tableContainer" @scroll="onTableScroll"><table class="stk-table-main"><thead><tr><th v-for="col in columns" :key="col.dataIndex" :data-col-key="col.dataIndex">{{ col.title || '--' }}</th></tr></thead><tbody><tr :style="{ height: `${virtualScroll.offsetTop}px` }"></tr><tr v-for="row in virtual_dataSourcePart" :key="row.id"><td v-for="col in columns" :key="col.dataIndex">{{ row[col.dataIndex] || '--' }}</td></tr><tr :style="{ height: `${virtual_offsetBottom}px` }"></tr></tbody></table></div>
</template>
StkTable.svelte
<script>import { onMount } from 'svelte';import '../stk-table/stk-table.less';export let style = '';export let virtual = true;let tableContainer;let virtualScroll = {containerHeight: 0,startIndex: 0, // 数组开始位置rowHeight: 28,offsetTop: 0, // 表格定位上边距scrollTop: 0, // 纵向滚动条位置,用于判断是横向滚动还是纵向};export let columns = [{ dataIndex: 'id', title: 'ID' },{ dataIndex: 'name', title: 'Name' },];export let dataSource = [];$: dataSourceCopy = [...dataSource];/** 数据量大于2页才开始虚拟滚动*//** 虚拟滚动展示的行数 */$: virtual_pageSize = Math.ceil(virtualScroll.containerHeight / virtualScroll.rowHeight) + 1; // 这里最终+1,因为headless=true无头时,需要上下各预渲染一行。$: virtual_on = virtual && dataSourceCopy.length > virtual_pageSize * 2;/** 虚拟滚动展示的行 */$: virtual_dataSourcePart = virtual_on? dataSourceCopy.slice(virtualScroll.startIndex, virtualScroll.startIndex + virtual_pageSize): dataSourceCopy;/** 虚拟表格定位下边距*/$: virtual_offsetBottom = virtual_on ?(dataSourceCopy.length - virtualScroll.startIndex - virtual_dataSourcePart.length) * virtualScroll.rowHeight : 0;onMount(() => {initVirtualScroll();});/*** 初始化虚拟滚动参数* @param {number} [height] 虚拟滚动的高度*/function initVirtualScroll(height) {initVirtualScrollY(height);// this.initVirtualScrollX();}/*** 初始化Y虚拟滚动参数* @param {number} [height] 虚拟滚动的高度*/function initVirtualScrollY(height) {if(virtual_on){virtualScroll.containerHeight = typeof height === 'number' ? height : tableContainer?.offsetHeight;virtualScroll = virtualScroll;updateVirtualScrollY(tableContainer?.scrollTop);}}/** 通过滚动条位置,计算虚拟滚动的参数 */function updateVirtualScrollY(sTop = 0) {const { rowHeight } = virtualScroll;const startIndex = Math.floor(sTop / rowHeight);Object.assign(virtualScroll, {startIndex,offsetTop: startIndex * rowHeight, // startIndex之前的高度});}function onTableScroll(e) {if (!e?.target) return;// 此处可优化,因为访问e.target.scrollXX消耗性能const { scrollTop, scrollLeft } = e.target;// 纵向滚动有变化if (scrollTop !== virtualScroll.scrollTop) virtualScroll.scrollTop = scrollTop;if (virtual_on) {updateVirtualScrollY(scrollTop);}}
</script><div class="stk-table" bind:this={tableContainer} {style} on:scroll={onTableScroll}><table class="stk-table-main"><thead><tr>{#each columns as col (col.dataIndex)}<th data-col-key={col.dataIndex}>{col.title || '--'}</th>{/each}</tr></thead><tbody><tr style="height: {`${virtualScroll.offsetTop}px`}"></tr>{#each virtual_dataSourcePart as row (row.id)}<tr>{#each columns as col (col.dataIndex)}<td>{row[col.dataIndex] || '--'}</td>{/each}</tr>{/each}<tr style="height: {`${virtual_offsetBottom}px`}"></tr></tbody></table>
</div>
StkTable.jsx (solid-js)
import { For, createSignal, onMount } from "solid-js";
import '../stk-table/stk-table.less';export function StkTable(props) {let tableContainer;const [virtualScroll, setVirtualScroll] = createSignal({containerHeight: 0,startIndex: 0, // 数组开始位置rowHeight: 28,offsetTop: 0, // 表格定位上边距scrollTop: 0, // 纵向滚动条位置,用于判断是横向滚动还是纵向});const [dataSourceCopy, setDataSourceCopy] = createSignal([]);const virtual_pageSize = () => {const vs = virtualScroll();return Math.ceil(vs.containerHeight / vs.rowHeight) + 1}; // 这里最终+1,因为headless=true无头时,需要上下各预渲染一行。const virtual_on = () => props.virtual && dataSourceCopy().length > virtual_pageSize() * 2;/** 虚拟滚动展示的行 */const virtual_dataSourcePart = () => {const vs = virtualScroll();const pageSize = virtual_pageSize();console.log(vs, pageSize)return virtual_on()? dataSourceCopy().slice(vs.startIndex, vs.startIndex + pageSize): dataSourceCopy()};/** 虚拟表格定位下边距*/const virtual_offsetBottom = () => virtual_on() ? (dataSourceCopy().length - virtualScroll().startIndex - virtual_dataSourcePart().length) * virtualScroll().rowHeight : 0;onMount(() => {setDataSourceCopy([...props.dataSource]);initVirtualScroll();});/*** 初始化虚拟滚动参数* @param {number} [height] 虚拟滚动的高度*/function initVirtualScroll(height) {initVirtualScrollY(height);// this.initVirtualScrollX();}/*** 初始化Y虚拟滚动参数* @param {number} [height] 虚拟滚动的高度*/function initVirtualScrollY(height) {if (virtual_on()) {const vs = virtualScroll()vs.containerHeight = typeof height === 'number' ? height : tableContainer?.offsetHeight;setVirtualScroll(vs);updateVirtualScrollY(tableContainer?.scrollTop);}}/** 通过滚动条位置,计算虚拟滚动的参数 */function updateVirtualScrollY(sTop = 0) {let vs = virtualScroll();const startIndex = Math.floor(sTop / vs.rowHeight);Object.assign(vs, {startIndex,offsetTop: startIndex * vs.rowHeight, // startIndex之前的高度});setVirtualScroll({...vs});// 必须扩展运算,否则不触发更新}function onTableScroll(e) {if (!e?.target) return;// 此处可优化,因为访问e.target.scrollXX消耗性能const { scrollTop, scrollLeft } = e.target;const vs = virtualScroll()// 纵向滚动有变化if (scrollTop !== vs.scrollTop) {vs.scrollTop = scrollTop;setVirtualScroll(vs);}if (virtual_on()) {updateVirtualScrollY(scrollTop);}}return <div class="stk-table" ref={tableContainer} style={props.style} onScroll={onTableScroll}><table class="stk-table-main"><thead><tr><For each={props.columns}>{(col) =><th data-col-key={col.dataIndex}>{col.title || '--'}</th>}</For></tr></thead><tbody><tr style={{ height: `${virtualScroll().offsetTop}px` }}></tr><For each={virtual_dataSourcePart()}>{(row) =><tr><For each={props.columns}>{(col) => <td>{row[col.dataIndex] || '--'}</td>}</For></tr>}</For><tr style={{ height: `${virtual_offsetBottom()}px` }}></tr></tbody></table></div>
}
style.less
src/StkTable/style.less · JA+/stk-table-vue
性能比较
比较虚拟滚动性能。
通过长按键盘↓键滚动,且将电脑cpu速度调至减速6x![]()
观察浏览器开发者面板performance标签下的任务耗时。
vue任务

svelte任务

solid任务
结论
观察solid任务的超时情况(红色部分),大体比vue与svelte要少。
solid与svelte 相较vue在虚拟滚动情况下,未能观察到明显优势。
相关文章:
vue svelte solid 虚拟滚动性能对比
前言 由于svelte solid 两大无虚拟DOM框架,由于其性能好,在前端越来越有影响力。 因此本次想要验证,这三个框架关于实现表格虚拟滚动的性能。 比较版本 vue3.4.21svelte4.2.12solid-js1.8.15 比较代码 这里使用了我的 stk-table-vue(np…...
IDEA中新增文件,弹出框提示是否添加到Git点错了,怎么重新设置?
打开一个配置了Git的项目,新增一个文件,会弹出下面这个框。提示是否将新增的文件交给Git管理。 一般来说,会选择ADD,并勾选Dont ask agin,添加并不再询问。如果不小心点错了,可在IDEA中重新设置(…...
LV15 day5 字符设备驱动读写操作实现
一、读操作实现 ssize_t xxx_read(struct file *filp, char __user *pbuf, size_t count, loff_t *ppos); 完成功能:读取设备产生的数据 参数: filp:指向open产生的struct file类型的对象,表示本次read对应的那次open pbuf&#…...
Uninty 鼠标点击(摄像机发出射线-检测位置)
平面来触发碰撞,胶囊用红色材质方便观察。 脚本挂载到胶囊上方便操作。 目前实现的功能,鼠标左键点击,胶囊就移动到那个位置上。 using System.Collections; using System.Collections.Generic; using UnityEngine;public class c6 : MonoBe…...
描述下Vue自定义指令
描述下Vue自定义指令 (1)自定义指令基本内容(2)使用场景(3)使用案例 在 Vue2.0 中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 DOM 元素进行底层…...
2024.3.7
作业: 1、OSI的七层网络模型有哪些,每一层有什么作用? (1)应用层 负责处理不同应用程序之间的通信,需要满足提供的协议,确保数据发送方和接收方的正确 (2)表示层…...
this.$watch 侦听器 和 停止侦听器
使用组件实例的$watch()方法来命令式地创建一个侦听器; 它还允许你提前停止该侦听器 语法:this.$watch(data, method, object) 1. data:侦听的数据源,类型为String 2. method:回调函数&#x…...
P1030 [NOIP2001 普及组] 求先序排列题解
题目 给出一棵二叉树的中序与后序排列。求出它的先序排列。(约定树结点用不同的大写字母表示,且二叉树的节点个数≤8)。 输入输出格式 输入格式 共两行,均为大写字母组成的字符串,表示一棵二叉树的中序与后序排列。…...
【分布式】NCCL Split Tree kernel内实现情况 - 06
相关系列 【分布式】NCCL部署与测试 - 01 【分布式】入门级NCCL多机并行实践 - 02 【分布式】小白看Ring算法 - 03 【分布式】大模型分布式训练入门与实践 - 04 目录 相关系列概述1.1 Tree1.2 double binary tree初始化和拓扑2.1 Tree的初始化与差异2.2 ncclGetBtreeKernel内部…...
C语言深入学习 --- 4.自定义类型(结构体+枚举+联合)
第四章 自定义类型:结构体,枚举,联合 结构体 结构体类型的声明 结构的自引用 结构体变量的定义和初始化 结构体的内存对齐 结构体实现位段(位段的填充 和 可移植性) 枚举 枚举类型的定义 枚举的优点 枚举的使…...
AI自然语言中默认上下文长度4K 几K是什么意思?
环境: 4K 问题描述: AI自然语言中默认上下文长度4K 几K是什么意思? 解决方案: 在自然语言处理中,“k” 表示 “千”,是一种简写方式。当我们说 “4k” 时,实际上指的是 “4,000”。在上下文…...
vSphere 8考试认证题库 2024最新(VCP 8.0版本)
VMware VCP-DCV(2V0-21.23)认证考试题库,已全部更新,答案已经完成校对,完整题库请扫描上方二维码访问。正常考可以考到450分以上(满分500分,300分通过) An administrator is tasked …...
系统学习Python——装饰器:“私有“和“公有“属性案例-[装饰器参数、状态保持和外层作用域]
分类目录:《系统学习Python》总目录 文章《系统学习Python——装饰器:“私有“和“公有“属性案例-[实现私有属性]》中使用的类装饰器接受任意多个参数来命名私有属性。然而真正发生的情况是,参数传递给了Private函数,然后Private…...
星辰天合参与编制 国内首个可兼顾 AI 大模型训练的高性能计算存储标准正式发布
近日,在中国电子工业标准化技术协会高标委的支持和指导下,XSKY星辰天合作为核心成员参与编制的《高性能计算分布式存储系统技术要求》团体标准,在中国电子工业标准化技术协会网站正式发布。 该团体标准强调了分布式存储系统对包括传统高性能计…...
算法训练day38动态规划基础Leetcode509斐波纳切数70爬楼梯746使用最小花费爬楼梯
什么是动态规划 对于动态规划问题,我将拆解为如下五步曲,这五步都搞清楚了,才能说把动态规划真的掌握了! 确定dp数组(dp table)以及下标的含义确定递推公式dp数组如何初始化确定遍历顺序举例推导dp数组&a…...
Leetcode 206. 反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 示例 1: 输入:head [1,2,3,4,5] 输出:[5,4,3,2,1] 示例 2: 输入:head [1,2] 输出:[2,1] 示例 3: 输…...
电子科技大学课程《计算机网络系统》(持续更新)
前言 本校的课程课时有所缩减,因此可能出现与你学习的课程有所减少的情况,因此对其他学校的同学更多的作为参考作用。本文章适合学生的期中期末考试,以及想要考研电子科技大学的同学,电子科技大学同学请先看附言。 第一章 计算…...
HBase介绍、特点、应用场景、生态圈
目录: 一、HBase简介 二、NoSQL和关系型数据库对比 三、HBase特点 四、应用场景 五、HBase生态圈技术 一、HBase简介 HBase是一个领先的NoSQL数据库 是一个面向列存储的NoSQL数据库 是一个分布式Hash Map,底层数据是Key-Value格式 基于Coogle Big Table论文 使用HD…...
蓝桥杯错误记录
今天在做 小蜜蜂的综合案例的时候,数码管显示,有重影。 #include <STC15F2K60S2.H> unsigned char num; unsigned char code Duan[22]{0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x80, 0xc6,0xc0,0x86,0x8e,0xbf,0x7f,0XC1,0X8C,0…...
Spring-静态代理VS动态代理/实现代理ProxyFactory
文章目录 静态代理VS动态代理Spring实现代理ProxyFactory 工作中遇到问题整理动态代理异常com.sun.proxy.$Proxy0 cannot be cast to 静态代理VS动态代理 静态代理VS动态代理 参考URL: https://blog.csdn.net/qq_25881443/article/details/103245938 【java项目实战】代理模式…...
AI 视频创作系统:新媒体高效增收工具,AI 自动成片,持续输出优质内容
一、新媒体行业增收难,传统创作模式遇瓶颈如今做新媒体账号想要稳定盈利,离不开高频优质内容输出。但多数从业者普遍面临诸多难题:人工写脚本耗时费力,实拍剪辑流程繁琐,长期聘请专职人员开支巨大;内容产出…...
别再一行行读DXF了!用C#和netDxf库5分钟搞定CAD数据提取(附完整代码)
用C#和netDxf库高效解析DXF文件的实战指南 在CAD数据处理领域,DXF文件解析一直是开发者面临的常见挑战。传统的手动解析方法不仅耗时费力,还容易出错。本文将带你探索如何利用C#和netDxf库快速实现DXF文件的高效解析,彻底告别逐行读取的原始方…...
如何用VR-Reversal将3D VR视频高效转换为普通2D格式:完整实用指南
如何用VR-Reversal将3D VR视频高效转换为普通2D格式:完整实用指南 【免费下载链接】VR-reversal VR-Reversal - Player for conversion of 3D video to 2D with optional saving of head tracking data and rendering out of 2D copies. 项目地址: https://gitcod…...
基于MCP协议构建安全AI支付工具:从原理到实践
1. 项目概述与核心价值最近在折腾AI智能体开发,特别是想给Claude Desktop这类工具增加点“超能力”,比如让它能直接帮我处理支付、查询订单状态,甚至自动对账。这想法听起来挺酷,但真动手去实现,发现最大的拦路虎不是写…...
通过环境变量安全配置Taotoken密钥实现跨平台开发
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 通过环境变量安全配置Taotoken密钥实现跨平台开发 在开发过程中,将API密钥等敏感信息硬编码在源代码中是常见的安全隐患…...
轻量级代码生成模型nanocoder:边缘部署与高效微调实战
1. 项目概述:一个为边缘而生的高效代码生成模型最近在折腾一些边缘设备上的AI应用,比如在树莓派或者Jetson Nano上跑一些轻量级的代码补全工具,发现市面上那些动辄几十亿参数的大模型根本塞不进去,跑起来也慢得让人心焦。就在这个…...
如何快速掌握哔哩下载姬:B站视频下载的终极免费解决方案
如何快速掌握哔哩下载姬:B站视频下载的终极免费解决方案 【免费下载链接】downkyi 哔哩下载姬downkyi,哔哩哔哩网站视频下载工具,支持批量下载,支持8K、HDR、杜比视界,提供工具箱(音视频提取、去水印等&…...
3步解决网盘下载限速难题:一站式直链解析工具实战指南
3步解决网盘下载限速难题:一站式直链解析工具实战指南 【免费下载链接】netdisk-fast-download 聚合多种主流网盘的直链解析下载服务, 一键解析下载,已支持夸克网盘/uc网盘/蓝奏云/蓝奏优享/小飞机盘/123云盘等. 支持文件夹分享解析. 体验地址: https://…...
ChatGPT与Notion深度整合实战手册(企业级私有化部署版):支持API密钥分级管控、审计日志追踪、GDPR合规配置
更多请点击: https://codechina.net 第一章:ChatGPT与Notion深度整合概述 ChatGPT 与 Notion 的深度整合正重塑个人知识管理与团队协作的工作流范式。二者分别代表当前最强大的语言理解能力与最灵活的结构化信息组织平台,其结合并非简单 API…...
从Mid360到自主移动:基于Fast-LIO与Move_Base的机器人导航实战拆解
1. Mid360激光雷达与Fast-LIO的适配实战 第一次拿到Livox Mid360激光雷达时,我完全被它36059的超大视场角惊艳到了。这款固态激光雷达不仅体积小巧,而且完全不用担心传统机械式雷达的电机磨损问题。但真正开始用它做SLAM时,才发现实物开发和仿…...

