Antv/s2 明细表 透视表实现和性能优化(一)

news2024/11/28 13:37:54

前言

        以我实际项目环境为准,vue+ts为技术框架,代码如果有什么不懂欢迎留言评论我会回复的

透视表

 定义文件

class PivotTableControl extends BaseControl {
  type = 'pivotTable';

  label = 'controls.chart.pivotTable';

  icon = 'tc-color-pivot-table';

  widget = () => ({
    ...super.widget(),
    // 数据源表单
    relationForm: null,
    // 数据范围
    range: 'all',
    // 行维度
    xDimension: [],
    // 列维度
    yDimension: [],
    // 指标
    metrics: [],
    // 过滤
    filter: null,
    // 图表配置
    typeConfig: chartDefaultConfigs()[this.type],
    // 背景
    background: null,
  });
}

        不用关心BaseControl有什么,看看几个特有的属性:

        xDimension: 行维度

        yDimension: 列维度

        metrics:指标

维度和指标

        业务设计的概念,方便我们理解和开发。先看例子

        一个行维度,一个指标

        

        一个行维度,两个指标

        

        两个行维度,一个指标

        

        到这里,可以看到,行维度就是分组依据,而指标就是对应分组依据的值。

        两个行维度,一个指标

        

在透视表里的维度和指标

         维度(行):分类依据,在「透视表」中指代的是透视表的行数将会以该组件数据进行划分。(如:行维度使用了字段「成员名称」,成员中包含5个不同的字符,于是透视表将会拆分成5行,每一行的命名标题将以「成员名称」的各字符命名。)

        维度(列):跟行维度一样,也是分类依据,在「透视表」中指代的是透视表的列数将会以该组件数据进行划分。(如:列维度使用字段「性别」其中学科包含男女,于是透视表将会划分成2列,分别对应男、女。)

        指标:指标是对维度的量化。在透视表中,他是指代除「列表头」、「行表头」以外的单元格展示的数值,而该数值是从属于相应行和列维度的数据汇总结果(如某单元格的表示的是行为某个成员,列是语文科目的成绩,代表该成员的语文成绩。)

实例:这里是一行维度一指标

 两行维度一指标

一行维度一列维度一指标

两行维度一列维度一指标

搞个复杂点的!两行维度两列维度两指标

S2

        项目里用的功能有这三个

import { PivotSheet, S2Event} from '@antv/s2';

        PivotSheet是它的渲染表格组件,S2Event是事件列表

        S2的设计是把我们行列维度的划分放在了角头中,行头列头都是划分的数据。

S2-封装

      我们要先封装一个配置好的S2表格组件。

const s2OptionsDefault = {
  width: 0,
  height: 0,
  interaction: {
    resize: {
      cornerCellHorizontal: true, // 角头水平
      rowCellVertical: true, // 行头垂直
      colCellHorizontal: true, // 列头水平
      colCellVertical: true, // 列头垂直
    },
    hoverHighlight: false, // 不开启hover聚光灯效果, 提高性能
    hoverFocus: false,
    brushSelection: false,
    multiSelection: false,
    rangeSelection: false,
    enableCopy: true,
  },
  pagination: {
    pageSize: 100,
  },
  frozenRowHeader: true,
  style: {
    cellCfg: {
      height: 34,
    },
    colCfg: {
      height: 34,
    },
  },
  hierarchyType: 'grid',
  conditions: {},
};
const s2DataConfigDefault = {
  fields: {
    rows: [],
    columns: [],
    values: [],
    valueInCols: true,
  },
  meta: [],
  data: [],
};

        先写好自己想要的默认配置。创建一个代理类VirtualTableProxy 

export class VirtualTableProxy {
  #instance;
  #options;
  #dataConfig;
  // 实例
  get instance() {
    return this.#instance;
  }

  // 配置
  get options() {
    return this.#options;
  }

  // 数据
  get dataConfig() {
    return this.#dataConfig;
  }

  setOptions(path, value) {
    if (!this.#options || typeof path === 'object') {
      this.#options = path;
    } else {
      set(this.#options, path, value);
    }
  }

  setDataConfig(path, value) {
    if (!this.#dataConfig || typeof path === 'object') {
      this.#dataConfig = path;
    } else {
      set(this.#dataConfig, path, value);
    }
  }

  setInstance(instance) {
    this.#instance = instance;
  }
}

        这个类有什么用,我们后面讲

// s2-component

created() {
    // 初始化实例
    this.VT = new VirtualTableProxy();
    this.VT.setOptions(deepClone(s2OptionsDefault));
    this.VT.setDataConfig(deepClone(s2DataConfigDefault));
}

       render函数:

render() {
    return (
      <div>
        <div id={this.componentId} class={this.$style.container}></div>
       </div>
    );
}
componentId = `container_${this._uid}`;

        这里的uid你可以理解为组件的hash标识

        等待模板渲染后:

  mounted() {
    const container = document.getElementById(this.componentId);
    this.container = container;
    this.VT.setInstance(
      new PivotSheet(container, this.VT.options, this.VT.dataConfig),
    );
 }

        这里的setIntance只是把这个s2生成的表格实例存放到VirtualTableProxy 类里的私有属性而已

        注册S2事件监听

mounted() {
   ....
   this.VT.instance.on(S2Event.DATA_CELL_CLICK, this.dataCellClick);
   this.VT.instance.on(S2Event.LAYOUT_AFTER_RENDER, () => {
      this.$emit('finishRender');
    });
}

            其实几乎就是把事件抛出去给外部处理,毕竟s2-component只是一个加载组件而已

            到这里,这个组件已经基本完成。我们做的只是创建一个代理类用来存储配置、表格实例,同时在组件上渲染了一个初始化的表格。那还差什么?数据注入。

        从外部调用S2-component

        index.vue调用s2-component

// 透视表组件主体文件 index.vue
<template>
  <div>
    <s2-component
      ref="virtualTableS2"
      :fixed-header-y="widget.fixedY"
      :fixed-header-x="widget.fixedX"
      :field="field"
      :relationFormFieldMap="relationFormFieldMap"
      :total="total"
      :totalText="totalText"
      :metricsMaxValues="metricsMaxValues"
      @finishRender="finishRender"
    />
   </div>
</template>

        

created() {
    this.init();
}
init() {
    ...
     // 转换数据结构
    const tableData = this.generateCommonData({
      cornerHeader: columnItem,
      colHeader: columnDimension,
      rowDimensionCategory: rowItem,
      metricsCategory: indicatorItem,
      rowHeader: rowDimension,
      dataCell: data,
      summaryResult: {
        rowSummary: result.summaryResult?.rowSummary || [],
        colSummary: result.summaryResult?.colSummary || [],
      },
   });
    ...
}

        前面都是业务逻辑,将接口端的数据转换成S2表格支持的数据格式。like be...

                这个数据结构呢,类似于上文提到的s2DataConfigDefault。不出意外的话,tableData 将会覆盖原来的s2DataConfigDefault。

init() {
    // 避免Vue依赖收集
    this.data = new PivotTableProxy(tableData);
}
export class PivotTableProxy {
  #data;
  constructor(data) {
    this.#data = data;
  }
  get data() {
    return this.#data;
  }
}

        往下看,你会发现this.data被赋值了给一个类,而tableDate作为类的私有属性被储存起来。这里要引申一个概念,Vue的依赖收集。

Vue的依赖收集和性能问题-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/weixin_42274805/article/details/128685452?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522169674882416800180624606%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=169674882416800180624606&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-128685452-null-null.nonecase&utm_term=%E4%BE%9D%E8%B5%96%E6%94%B6%E9%9B%86&spm=1018.2226.3001.4450        不懂可以看我这篇,这里为什么要绕开Vue的依赖收集呢。因为依赖收集需要对数据元进行递归绑定。当数据元数据量过大(例如明细表或透视表数十万条数据),会严重影响前端的性能效率。这么去藏值会让效率提升很多哦,特别是几十万条数据的情况下。但是,透视表的渲染将由前端自行控制,而不是由Vue框架的响应式决定。

init() {
    ...// 接上文
    // 手动渲染透视表
    this.$nextTick(() => {
      this.$refs.virtualTableS2?.setData(this.data.data);
    });
    
}

        看到这里,就该回去S2-component里补完数据注入逻辑啦

S2-封装-数据注入

        上面提到的setData方法。我们来看看怎么实现

 setData(pivotTableData) {
    const {
      fields: { rows, columns, values },
      meta,
      data,
    } = pivotTableData;
    // 配置
    this.pagination.total = this.total;
    this.VT.setOptions('frozenRowHeader', this.fixedHeaderY);
    this.VT.setOptions('style.cellCfg.height', this.field.widget.lineHeight);
    this.VT.setOptions('style.colCfg.height', this.field.widget.lineHeight);
    this.VT.setOptions('hierarchyType', this.field.widget.hierarchyType);

    // 数据
    this.VT.setDataConfig('fields.rows', rows);
    this.VT.setDataConfig('fields.columns', columns);
    this.VT.setDataConfig('fields.values', values);
    this.VT.setDataConfig('meta', meta);
    this.VT.setDataConfig('data', data);
    this.reRender();
  }

        操作很简单,根据数据去更改VT实例里的属性配置

  // 重新渲染S2
  reRender() {
    this.VT.instance.setOptions(this.VT.options);
    this.VT.instance.setDataCfg(this.VT.dataConfig);
    this.VT.instance.render(true);
  }

        上调用VT实例里的S2表格组件实例,填充配置和数据配置,并执行渲染。

        看到这里,大家应该知道为什么要设置VirtualTableProxy类了吧,一方面是为了绕开数据绑定,第二方面是为了把数据、配置、视图模块化。

性能对比

        上面提到过,绕过Vue的依赖收集会有巨大的性能提升。现在来看看效果:

        为了快速定位方法,我把上面的init改成testIn。打开浏览器的性能标签页,开始性能检测。

        这是绕过的:

 this.data = new PivotTableProxy(tableData);

       看到testin方法一共耗时1.03秒,其中this.generateCommonData占用了1.03秒,这个方法就是我们前端遍历转换数据的。

        这是没有绕过的:

this.data = tableData

         此时testin方法耗时了1.91秒,其中generateCommonData耗时仅1.08秒,与上面接近,那么多出来的800多毫秒,是黄色部分组成的。

        看到observer和defineReactive了吗?想到什么?Vue2的双向绑定啊!!Vue对data数据递归后逐个绑定观察者,上面的实验仅有124条数据,便要产生800毫秒的延迟!如果是124万条数据呢?不用怀疑,直接卡死!所以我们才要绕过Vue的依赖收集

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

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

相关文章

Linux设备驱动的精髓在哪?为何感觉写驱动就像写八股文?

Linux设备驱动的精髓在哪&#xff1f;为何感觉写驱动就像写八股文&#xff1f; 话题背景&#xff1a;随着互联网尤其是移动互联网的发展&#xff0c;Android手机操作系统得到了广泛应用&#xff0c;而Android系统是基于Linux系统开发的。另外&#xff0c;大数据、云计算等技术也…

7321-2017 定形耐火制品试样制备方法

声明 本文是学习GB-T 7321-2017 定形耐火制品试样制备方法.pdf而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了定形耐火制品制样的定义、制样部位的确定原则和试样的制备。 本标准适用于定形耐火制品试样的制备。 2 规范性引用文…

AI:10-基于TensorFlow的玉米病害识别

玉米是世界上最重要的粮食作物之一,然而,玉米病害对其产量和质量造成了严重威胁。传统的病害识别方法通常依赖于人工观察和经验判断,效率低下且易受主观因素影响。近年来,基于深度学习的图像识别技术在农业领域取得了显著进展,为玉米病害的快速、准确识别提供了新的解决方…

【虚拟机】根据已有IP获取当前网段的主机范围

虚拟机有的时候会需要自己手动分配IP&#xff0c;此时无论是和宿主主机通信、还是访问外网&#xff0c;都需要保证和宿主主机在一个网段。现在已知宿主主机的 IP 和子网掩码&#xff0c;需要知道宿主主机所处网段可以分配哪些IP。 假设宿主主机 IP 为172.20.10.2&#xff0c;子…

[补题记录] Atcoder Beginner Contest 323(E、F)

URL&#xff1a;https://atcoder.jp/contests/abc323 目录 E Problem/题意 Thought/思路 Code/代码 F Problem/题意 Thought/思路 Code/代码 E Problem/题意 有 N 首歌曲&#xff0c;每首歌曲时长为 Ti。每次随机播放一首歌曲&#xff0c;问在 X 0.5 这一时刻&#x…

ict在线测试设备功能如何作用与芯片静态功耗测试,提升性能和稳定性

在芯片的众多测试项目中芯片的功耗测试可谓重中之重&#xff0c;因为芯片的功耗不仅关系着芯片的整体工作性能也对芯片的效率有着非常重大的影响。芯片的功耗测试包括动态功耗和静态功耗。 芯片静态功耗是什么? 芯片的静态功耗也叫做芯片静态电流&#xff0c;它是芯片测试中的…

时尚品牌为什么需要软文推广?

时尚行业一直以其创造力引领着全球消费市场的发展&#xff0c;随着时尚品牌市场的竞争愈发激烈&#xff0c;时尚行业的营销方式必须更具战略性&#xff0c;才能在众多品牌中脱颖而出&#xff0c;在最近几年&#xff0c;软文营销一直是时尚行业常用的营销手段之一&#xff0c;为…

二维码安全技术交底如何做

安全技术交底是一项非常重要的安全管理工作&#xff0c;对于保障施工现场的安全和人员的生命安全具有不可替代的作用。二维码安全技术交底可以逐级落实、责任到人、有据可查、是目前最方便、实用的交底方式&#xff0c;下面我们可以参考利用凡尔码搭建建筑管理系统来管理安全技…

【力扣每日一题】2023.10.8 股票价格波动

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 这道题是程序设计题&#xff0c;要我们实现一个类&#xff0c;一共是四个功能&#xff0c;第一个是给一个时间戳和价格&#xff0c;表示该…

C++11 Qt QFutureWatcher lambda

目录 Lambda 介绍 【QT】Qt之QFutureWatcher 简述 传参&#xff1a; 还可以使用 QProgressDialog 作为阻堵 函数&#xff0c;变成同步&#xff1b; 完成后&#xff0c;关闭&#xff1b; MyQProgressDialog 效果&#xff1a; Lambda 介绍 Lambda 函数也叫匿名函数&…

Hadoop使用hdfs指令查看hdfs目录的根目录显示被拒

背景 分布式部署hadoop,服务机只有namenode节点,主机包含其他所有节点 主机关机后,没有停止所有节点,导致服务机namenode继续保存 再次开启主机hadoop,使用hdfs查看hdfs根目录的时候显示访问被拒 解决方案 1.主机再次开启hadoop并继续执行关闭 2.服务器再次开启hadoop并继…

强化学习入门

简介 什么是强化学习 强化学习是一种机器学习方法&#xff0c;它可以帮助计算机学会在不断尝试和经验积累中做出最佳决策。用通俗的方式来说&#xff0c;强化学习就像是训练一只宠物狗学会做任务一样。 想象一只狗要学会取球。一开始&#xff0c;它可能不知道该怎么做&#…

回顾Softing 2023工博之旅精彩瞬间

2023年9月23日&#xff0c;为期5天的第23届中国国际工业博览会&#xff08;CIIF&#xff09;于上海国家会展中心圆满落幕。Softing作为PROFIBUS创始人之一&#xff0c;德国工业4.0的领军企业之一&#xff0c;在本次展会上向大家呈现了众多工业自动化及IT网络方面的领先产品及方…

深度学习笔记之优化算法(四)Nesterov动量方法的简单认识

机器学习笔记之优化算法——Nesterov动量方法的简单认识 引言回顾&#xff1a;梯度下降法与动量法Nesterov动量法Nesterov动量法的算法过程描述总结 引言 上一节对动量法进行了简单认识&#xff0c;本节将介绍 Nesterov \text{Nesterov} Nesterov动量方法。 回顾&#xff1a;…

在SOLIDWORKS搭建一个简易的履带式机器人

文章目录 前言一、构建模型基本单元二、搭建车体模块三.插入轮子4.构建履带 前言 趁着十一假期&#xff0c;在solidworks中搭建了一个履带式机器人小车&#xff0c;计划将其应用在gazebo中完成多机器人编队的仿真。 一、构建模型基本单元 构建底板&#xff08;a面&#xff09…

第六讲:构建类的事件(下)

《VBA中类的解读及应用》教程【10165646】是我推出的第五套教程&#xff0c;目前已经是第一版修订了。这套教程定位于最高级&#xff0c;是学完初级&#xff0c;中级后的教程。 类&#xff0c;是非常抽象的&#xff0c;更具研究的价值。随着我们学习、应用VBA的深入&#xff0…

.some方法、vh、多列布局、DNS域名解析过程、空页面文字内容渲染

nodeEndTime.some((time) > !!time&#xff09; 这个方法主要用来判断nodeEndTime这个数组中是否有至少一个非假值的元素。它会遍历 nodeEndTime 数组中的每个元素&#xff0c;如果至少有一个元素是真值&#xff08;不是 null、undefined、0、false 或空字符串等假值&#x…

Flutter学习笔记

此篇文章用来记录学习Flutter 和 Dart 相关知识 零.Dart基本数据类型 Dart 是一种静态类型的编程语言&#xff0c;它提供了一系列基本数据类型&#xff0c;用于存储和操作不同种类的数据。以下是 Dart 中的一些基本数据类型以及它们的详细介绍&#xff1a; 1. 整数类型&#…

python学习——各种模块argparse、os、sys、time、re、sched、函数体

python学习——各种模块argparse、os、sys、time、re、sched、函数体 各种模块学习1. python脚本2. argparse模块&#xff1a;撰写帮助文档&#xff0c;命令行参数定义等3. os模块&#xff1a;用于文件/目录路径或名字的获取4. sys模块&#xff1a;用于对命令行参数进行获取处理…

车企“新四化”之——安全高效的电动汽车和混动汽车高压测量方案

一 背景 汽车行业正经历着“新四化”从概念向实际转化的过程&#xff08;新四化即电动化、智能化、网络化、共享化&#xff09;&#xff0c;各大车企也将调整发展布局&#xff0c;而混合动力及电动汽车自然就成为了主要关注点。 对于混合动力汽车&#xff08;PHEV&#xff09…