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,一经查实,立即删除!

相关文章

java框架八股

spring框架中单例bean是线程安全的吗 不是线程安全的&#xff0c;spring框架并没有对单例bean进行任何多线程的封装处理。通常在项目中使用的spring bean都是不可变的状态。如果bean有多种状态的话&#xff0c;就需要自行保证线程安全。 什么是AOP&#xff1f; aop是面向切面编…

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版本及更早版本。 该…

C++三级专项 流感传染

时间限制&#xff1a;1000 内存限制&#xff1a;65536 有一批易感人群住在网格状的宿舍区内&#xff0c;宿舍区为n*n的矩阵&#xff0c;每个格点为一个房间&#xff0c;房间里可能住人&#xff0c;也可能空着。在第一天&#xff0c;有些房间里的人得了流感&#xff0c;以后每…

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

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

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

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

你知道便利店神秘顾客调研开展原因啊吗?

便利店神秘顾客调研的开展原因主要有以下几点&#xff1a; 1、评估服务质量和商品质量&#xff1a;通过神秘顾客的调研&#xff0c;可以客观地评估便利店的服务质量和商品质量&#xff0c;发现存在的问题和不足之处&#xff0c;为便利店提供改进的依据。 2、了解顾客满意度和…

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、数据…

学习Android的第二十天

目录 Android Toast 吐司 常量 常用方法 例子 Android Notification 状态栏通知 Notification 的基本布局 扩展布局 Notification ( 状态栏通知 ) 相关的方法 例子&#xff1a; 参考文档 Android AlertDialog 弹出框 Android Dialog 继承图谱 AlertDialog 几种常…

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

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

MySQL数据库主从复制与读写分离(一)

目录 前言 主从复制设置 配置主服务器 1. **编辑配置文件**&#xff1a; 2. **重启MySQL服务**&#xff1a; 3. **创建复制用户**&#xff1a; 4. **记录二进制日志文件位置**&#xff1a; 配置从服务器 1. **编辑配置文件**&#xff1a; 2. **重启MySQL服务**&#x…

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

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

nginx代理minio教程 避坑过的教程 避开SignatureDoesNotMatch

本次教程使用的是单机minio进行演示&#xff0c;集群minio也和这个差不多。 按照这个教程&#xff0c;可以避开nginx代理minio之后&#xff0c;只能访问文件&#xff0c;但是通过预签名url上传文件就会报SignatureDoesNotMatch的坑 暂定如下&#xff1a; 你已经下载好miniom…

Kubernetes operator 零散知识篇【持续更新中】

云原生学习路线导航页&#xff08;持续更新中&#xff09; 本文是 Kubernetes operator学习 系列零散知识篇&#xff0c;在学习过程中发现 operator开发 有很多零碎的知识&#xff0c;所以单独用一篇文章记录Kubernetes operator学习系列 快捷链接 Kubernetes operator 前置知识…