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在虚拟滚动情况下,未能观察到明显优势。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/721142.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

web坦克大战小游戏

H5小游戏源码、JS开发网页小游戏开源源码大合集。无需运行环境,解压后浏览器直接打开。有需要的订阅后,私信本人,发源码,含60+小游戏源码。如五子棋、象棋、植物大战僵尸、贪吃蛇、飞机大战、坦克大战、开心消消乐、扑鱼达人、扫雷、打地鼠、斗地主等等。 <!DOCTYPE htm…

【图论】Dijkstra 算法求最短路 - 构建邻接矩阵(带权无向图)

文章目录 例题&#xff1a;到达目的地的方案数题目描述代码与解题思路构建带权无向图的邻接矩阵 例题&#xff1a;到达目的地的方案数 题目链接&#xff1a;1976. 到达目的地的方案数 题目描述 代码与解题思路 func countPaths(n int, roads [][]int) int {g : make([][]int…

数据库和缓存如何保持一致性

目录 前言 更新数据库更新缓存&#xff1a; 1.在更新缓存前先加一个分布式锁 2.在更新完缓存时&#xff0c;给缓存加上较短的过期时间 Cache Aside策略 1.先删除缓存&#xff0c;再更新数据库 延迟双删 2.先更新数据库&#xff0c;再删除缓存 保证两个操作都能执行成功…

【译】WordPress Bricks主题安全漏洞曝光,25,000个安装受影响

WordPress的Bricks主题存在一个严重的安全漏洞,恶意威胁行为者正在积极利用该漏洞在易受攻击的安装上运行任意PHP代码。 该漏洞被跟踪为CVE-2024-25600(CVSS评分:9.8),使未经身份验证的攻击者能够实现远程代码执行。它影响Bricks的所有版本,包括1.9.6版本及更早版本。 该…

线程变量ThreadLocal用于解决多线程并发时访问共享变量的问题。

ThreadLocal介绍 ThreadLocal叫做线程变量&#xff0c;用于解决多线程并发时访问共享变量的问题。意思是ThreadLocal中填充的变量属于当前线程&#xff0c;该变量对其他线程而言是隔离的&#xff0c;也就是说该变量是当前线程独有的变量。ThreadLocal为变量在每个线程中都创建…

如何用生成式人工智能准备和制作吸引人的美食视频?

YouTube是一个全球性的视频分享平台&#xff0c;上面充满了各式各样的内容&#xff0c;其中美食内容因其视觉和味觉上的双重吸引而备受欢迎。作为一个想要进入这个领域的创作者&#xff0c;你需要知道如何准备和制作吸引人的美食视频。以下是一些基本步骤和技巧&#xff1a; 选…

UE4 Niagara 关卡3.4官方案例解析

Texture sampling is only supported on the GPU at the moment.(纹理采样目前仅在GPU上受支持) 效果&#xff1a;textures can be referenced within GPU particle systems。this demo maps a texture to a grid of particles&#xff08;纹理可以在GPU粒子系统中被引用这个演…

git使用教程14-Pycharm版本控制与分支管理

一、版本控制 1、版本控制介绍 &#xff08;1&#xff09;Version Control System 版本控制系统&#xff0c;简称VCS。 &#xff08;2&#xff09;版本控制系统分类&#xff1a; 集中式版本控制工具&#xff1a;SVN 分布式版本控制工具&#xff1a;Git 2、Pycharm 支持的版本…

Windows安装MySQL8.0详细步骤

目录 一、官网下载MySQL二、将压缩包解压到没有中文和空格的目录下三、设置配置文件四、配置环境变量五、安装初始化mysql服务 一、官网下载MySQL 进入MySQL官网&#xff1a;https://downloads.mysql.com/archives/community/&#xff0c;下载 Windows (x86, 64-bit), ZIP Arch…

项目管理中,项目经理如何搞定跨部门沟通与协作?

在项目管理中&#xff0c;跨部门沟通是一项至关重要的任务。项目经理作为项目的核心协调者&#xff0c;需要确保不同部门之间的顺畅沟通&#xff0c;以推动项目的顺利进行。项目经理如何搞定跨部门沟通&#xff1f; 实际案例&#xff1a; 某公司正在开发一款智能家居系统&am…

深入探索Docker数据卷:实现容器持久化存储的完美方案(下)

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《Docker入门到精通》 《k8s入门到实战》&#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 四、Docker数据卷的高级管理 1、数据卷的生命周期管理 2、数据…

如何做代币分析:以 CRO 币为例

作者&#xff1a;lesleyfootprint.network 编译&#xff1a;Mingfootprint.network 数据源&#xff1a;CRO Token Dashboard &#xff08;仅包括以太坊数据&#xff09; 在加密货币和数字资产领域&#xff0c;代币分析起着至关重要的作用。代币分析指的是深入研究与代币相关…

【Python】进阶学习:pandas--rename()用法详解

【Python】进阶学习&#xff1a;pandas-- rename()用法详解 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448; 希望得到您的…

基于SpringBoot的物业管理系统

** &#x1f345;点赞收藏关注 → 私信领取本源代码、数据库&#x1f345; 本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目希望你能有所收获&#xff0c;少走一些弯路。&#x1f345;关注我不迷路&#x1f345;** 一 、设计说明 1.1 研究…

Springboot 过滤器、拦截器、全局异常处理

Springboot 过滤器、拦截器、全局异常处理 一 过滤器&#xff08;Filter&#xff09; 过滤器是JavaWeb三大组件&#xff08;Servlet&#xff0c;Filter&#xff0c;Listener&#xff09;之一。 Filter可以把对资源的请求拦截下来&#xff0c;从而实现一些功能。 注意&#…

低代码工具APEX的入门使用(未包含安装)

第一次使用APEX是2019年&#xff0c;这个技术成名已久只是我了解的比较晚。请看Oracle ACE的网站&#xff0c;这就是用APEX做的。实际上有一次我看O记的人操作他们的办公流程&#xff0c;都是用APEX做的。 那一年&#xff0c;我用APEX做了一个CMDB的管理系统。那时候还没有流行…

美摄科技实时语音数字人解决方案

随着科技的飞速发展&#xff0c;数字人技术已经逐渐渗透到我们生活的各个角落。作为数字人技术的先驱者&#xff0c;美摄科技凭借其卓越的实时语音数字人解决方案&#xff0c;正引领着企业步入一个全新的交互时代。 美摄科技的实时语音数字人解决方案&#xff0c;是基于语音和…

HCIA-Datacom题库(自己整理分类的)_33_DHCP协议多选【7道题】

1.使用动态主机配置协议DHCP分配IP地址有哪些优点? 可以实现IP地址重复利用 工作量大且不好管理 配置信息发生变化(如DNS),只需要管理员在DHCP服务器上修改,方便统一管理 避免IP地址冲突 2.网络中部署了一台DHCP服务器,但是管理员发现部分主机并没有正确获取到该DHCP服务…

产品说明书模板帮你写好了,快来抄作业

想要写好产品说明书&#xff0c;想在网络上参考&#xff0c;信息繁杂且各有不同&#xff0c;东拼西凑也写不出一个好的产品说明书。这时候&#xff0c;如果有产品说明书模板&#xff0c;不仅可以可以提高工作效率&#xff0c;还能保持产品说明书的一致性、提供参考和指导&#…

Mysql删除重复项:力扣196. 删除重复的电子邮箱

题目链接&#xff1a;196. 删除重复的电子邮箱 - 力扣&#xff08;LeetCode&#xff09; 题目描述 sql语句 # Write your MySQL query statement below delete a from person as a inner join person as b where a.email b.email and a.id > b.id 思路&#xff1a;内连接…