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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vue3实现页面跳转功能

目标&#xff1a; 首页&#xff1a; 点击About后&#xff1a; 第一步&#xff1a;安装 Vue Router和创建你先 npm install vue-router4第二步&#xff1a;在router.js中设置路由 import { createRouter, createWebHistory } from vue-router; import Home from ./views/Home…

HTML超详细简介

HTML是什么 超文本标记语言&#xff08;HyperText Mark-up Language &#xff09;用来设计网页的标记语言用该语言编写的文件&#xff0c;以 .html或 .htm为后缀由浏览器解释执行不区分大小写&#xff0c;建议小写 HTML标签 HTML用于描述功能的符号成为“标签”标签都封装在…

b站小土堆pytorch学习记录—— P25-P26 网络模型的使用和修改、保存和读取

文章目录 一、修改1.方法2.代码 二、保存和读取1.方法2.代码&#xff08;1&#xff09;保存&#xff08;2&#xff09;加载 3.陷阱 一、修改 1.方法 add_module(name: str, module: Module) -> None name 是要添加的子模块的名称。 module 是要添加的子模块。 调用 add_m…

部署SpringBoot项目

方案一&#xff1a;纯手工部署 1&#xff0c;购买一台云服务器 这里我使用腾讯云&#xff0c;推荐Centos8/Centos7.6 2&#xff0c;安装springBoot项目所需要的环境 1&#xff0c;数据库单独安装在另一台服务器上&#xff0c;只需要修改IP地址即可 2&#xff0c;安装jdk yum…

泛型 --java学习笔记

什么是泛型 定义类、接口、方法时&#xff0c;同时声明了一个或者多个类型变量&#xff08;如&#xff1a;<E>&#xff09;&#xff0c;称为泛型类、泛型接口&#xff0c;泛型方法、它们统称为泛型 可以理解为扑克牌中的癞子&#xff0c;给它什么类型它就是什么类型 如…

如何将中科方德桌面操作系统加入Windows域

往期文章&#xff1a;自定义SSH客户端连接时的显示信息 | 统信UOS | 麒麟KYLINOS Hello&#xff0c;大家好啊&#xff0c;今天我非常高兴地给大家带来一篇关于如何将中科方德桌面操作系统加入Windows域的教程文章。对于使用中科方德桌面操作系统的用户来说&#xff0c;将其加入…

TSINGSEE配电房/配电站/变电站远程视频智能监管、无人值守方案

一、背景需求分析 随着社会的快速发展和科技进步&#xff0c;电力作为现代社会的核心驱动力&#xff0c;其稳定运行与安全管理变得愈发重要。特别是在配电房这一关键环节中&#xff0c;实施高效的远程视频智能监管方案&#xff0c;不仅能够有效提升电力供应的可靠性&#xff0…

模拟实现std::string类(包含完整、分文件程序)

std库中的string是一个类&#xff0c;对string的模拟实现&#xff0c;既可以复习类的特性&#xff0c;也可以加深对std::string的理解。 &#x1f308;一、搭建框架 ☀️1.新命名空间 本质上string是一个储存在库std里面的类&#xff0c;现在需要模拟实现一个string类&#…

Linux:kubernetes(k8s)探针ReadinessProbe的使用(9)

本章yaml文件是根据之前文章迭代修改过来的 先将之前的pod删除&#xff0c;然后使用下面这个yaml进行生成pod apiVersion: v1 # api文档版本 kind: Pod # 资源对象类型 metadata: # pod相关的元数据&#xff0c;用于描述pod的数据name: nginx-po # pod名称labels: # pod的标…

好物周刊#46:在线工具箱

https://github.com/cunyu1943 村雨遥的好物周刊&#xff0c;记录每周看到的有价值的信息&#xff0c;主要针对计算机领域&#xff0c;每周五发布。 一、项目 1. twelvet 一款基于 Spring Cloud Alibaba 的权限管理系统&#xff0c;集成市面上流行库&#xff0c;可以作用为快…

【电路笔记】-NPN晶体管

NPN晶体管 文章目录 NPN晶体管1、概述2、双极NPN晶体管配置3、NPN晶体管中的α和β关系4、示例5、共发射极配置1、概述 NPN 晶体管是三端三层器件,可用作放大器或电子开关。 在前面的文章中,我们看到标准双极晶体管或 BJT 有两种基本形式。 NPN(负-正-负)配置和PNP(正-负…

稀碎从零算法笔记Day11-LeetCode:有效的字母异位词

题型&#xff1a;字符串、哈希表、排序 链接&#xff1a;242. 有效的字母异位词 - 力扣&#xff08;LeetCode&#xff09; 来源&#xff1a;LeetCode 题目描述 给定两个字符串 s 和 t &#xff0c;编写一个函数来判断 t 是否是 s 的字母异位词。 注意&#xff1a;若 s 和 …

电子签名签章:重塑企业办公方式,开启智能合约新时代!

在现代社会&#xff0c;随着科技的发展&#xff0c;纸质文件逐渐被电子文件所取代&#xff0c;传统的签名方式也面临着数字化的转型。电子签名签章云服务就是在这样的背景下应运而生的一种新型技术服务&#xff0c;它结合了云计算、大数据、人工智能等前沿技术&#xff0c;为用…

尤雨溪:Vue 未来展望新的一轮

十年&#xff0c;一个既漫长又短暂的时光跨度&#xff0c;对于技术世界来说&#xff0c;更是沧海桑田的瞬间。在这十年里&#xff0c;Vue.js 从无到有&#xff0c;从默默无闻到蜚声全球&#xff0c;不仅改变了前端开发的面貌&#xff0c;更成为了无数开发者手中的得力工具。 在…

程序逻辑控制

1.java的三大结构 可以说java的这三大结构包括其中的语句跟c语言上的基本上都是一样的。现在就当重新复习一遍吧&#xff01; 1.顺序结构 2.分支结构 if语句 跟c语言的语法一模一样。就直接看文案了。 switch语句 java中的switch语句跟c语言中的switch几乎相同&#xff0c;…

2024【问题解决】Github 2024无法克隆git clone自从签了2F2安全协议之后

项目场景:ping通Github但没法clone–502 问题描述 提示:ping通Github但没法clone--502: 例如:git clone https://gitclone.com/l.git/*** $ git clone https://github.com/darrenpig/Yocto Cloning into Yocto_tutorial... fatal: unable to access https://gitclone.co…

mysql数据库入门到精通-Windows版本mysql安装(1)

文章目录 一、数据库介绍1.1、数据库概念1.2、为什么要使用数据库1.3、关系型数据库与非关系型数据库1.4、数据库术语1.5、mysql下载及地址 二、安装mysql数据库三、mysql的管理工具3.1、mysql command line client使用 四、SQL结构化查询语言4.1、SQL概述4.2、SQL发展4.3、SQL…

【CSP试题回顾】201503-3-节日

CSP-201503-3-节日 关键点&#xff1a;格式化输出 在C中&#xff0c;格式化输出通常利用iostream库中的功能&#xff0c;特别是iomanip头文件提供的一系列操作符。这些操作符用于控制输出格式&#xff0c;如宽度、填充、对齐方式等。在你提供的代码中&#xff0c;用于格式化输…

飞桨AI框架安装和使用示例

飞桨AI框架安装和使用示例 飞桨PaddlePaddle是非常流行的国产AI框架&#xff0c;让我们一起来动手实践吧&#xff01; 安装 飞桨安装参考页面&#xff1a;https://www.paddlepaddle.org.cn/install/quick?docurl/documentation/docs/zh/install/pip/linux-pip.html 在这个安…

乌鸡的身高

解法&#xff1a; 只需要看身高最高的乌鸡个数是否>2.若满足则除去当前这只乌鸡的最高身高都是最高身高。 若不满足则只需要看最高的和第二高的乌鸡。 #include<iostream> #include<vector> #include<algorithm> #include<cmath> using namespac…