请选择 进入手机版 | 继续访问电脑版
 找回密码
 立即注册

QQ登录

只需一步,快速开始

Matthew.Xue

超级版主

12

主题

1030

帖子

1690

积分

超级版主

Rank: 8Rank: 8

积分
1690
Matthew.Xue
超级版主   /  发表于:2025-5-30 14:14  /   查看:75  /  回复:0
本帖最后由 Matthew.Xue 于 2025-5-30 14:16 编辑

背景:
某国内的龙头快递企业,内部有大量的数据统计需求,并且希望这些数据可以在线上做数据的分析、报表等,所以他们采用了SpreadJS作为数据的展示、分析的控件。
简单介绍一下他们使用SpreadJS的方式,一个工作簿中会有若干个工作表,工作表分为两种类型,一种是纯数据类型,我们简称为数据页;另一种是根据数据页的数据做分析,简称为报表页。
报表页中包含了以数据页为数据源的的透视表,这个报表页是用户所真正关心的页面,利用透视表的强大功能,可以对数据做不同维度的分析、统计,从而得到我们关心的结论。所以在向后端保存的过程中,数据页就可以丢弃了(得益于数据透视表有缓存数据,报表页生成后可以独立于数据页存在),这样可以有效减小后台的存储压力和网络传输的压力。
用户在下一次生成相同报表时,可以用最新的数据页代替之前的数据页,再从报表页更新一次数据源,就可以轻松实现报表页的复用了。

遇到挑战
由于一个工作簿中本来就存在多个数据页,每个数据页的数据量非常大,多的能到百万级别,所以他们遇到了以下问题:
  • 在向数据页写数据的时候,他们采用了遍历单元格去setValue的方式,前端在拿到数据后,向数据页写数据耗时非常久。
  • 在向后台保存之前,删除这些数据页也耗时很久。

解决方案
其实上面两个问题都是数据量太大造成的,这是因为SpreadJS在进行setValue或者setArray时,SpreadJS的数据模型会为设置的新值创建新的数据节点。
举个例子,向100万个单元格写入数据,SpreadJS内部需要创建100万个行/列数据节点,而性能消耗的大头就是数据节点的创建,所以如果能将这一部分时间节省下来,整体的运行时间就会得到极大的优化。
那么如何节省这一部分时间呢?SpreadJS中有一个功能叫做数据绑定,数据绑定可以对表格起作用,对应的数据源是一个数组,数组的长度是多少,在数据绑定后,表格就有多少行,如果用数据绑定的方式来填充数据,能否让速度变快呢?
我们来实际测试一下吧,以20列,一万行的数据量为例,我们分别使用setArray和setDataSource的方式向其填充数据,看看他们分别耗时多久:
setArray耗时7.3秒

setDataSource耗时0.18秒

可以看到,在填充20万个单元格的数据量下,setDataSource耗时仅为setArray的四十分之一
如果将行数增加到十万行,列数仍为20行,那么setArray耗时41.5秒,setDataSource耗时0.6秒,占比为七十分之一,可以得到结论:数据量越大,setDataSource的优势越明显。
这是因为SpreadJS在进行数据绑定时,不需要为数据分配额外的存储空间,而是直接从数据源中根据对应的位置抽取值,去掉了最耗时的创建数据节点的步骤,这才让setDataSource有了如此优异的性能表现。
同样的,删除sheet时setDataSource也有非常明显的优势,感兴趣的读者可以自行测试。
填充数据的测试代码:
  1. let rowCount = 10000
  2. let colCount = 20
  3. sheet.setRowCount(rowCount + 10)
  4. sheet.setColumnCount(colCount + 10)
  5. let table = sheet.tables.add("Table1", 0, 0, rowCount + 1, colCount)

  6. // setDataSource
  7. document.getElementById("btn1").addEventListener("click", function () {
  8.     table.bindingPath("table")
  9.     for (let c = 0; c < colCount; c++) {
  10.         table.setColumnDataField(c, "Column" + c)
  11.     }
  12.     let data = []
  13.     for (let r = 0; r < rowCount; r++) {
  14.         let obj = {}
  15.         for (let c = 0; c < colCount; c++) {
  16.             obj["Column" + c] = Math.random()
  17.         }
  18.         data.push(obj)
  19.     }

  20.     console.time("setDataSource")
  21.     sheet.setDataSource(new GC.Spread.Sheets.Bindings.CellBindingSource({
  22.         table: data
  23.     }))
  24.     console.timeEnd("setDataSource")

  25. })
  26. // setArray
  27. document.getElementById("btn2").addEventListener("click", function () {
  28.     let arr = []
  29.     for (let r = 1; r < rowCount; r++) {
  30.         let obj = []
  31.         for (let c = 0; c < colCount; c++) {
  32.             obj.push(Math.random())
  33.         }
  34.         arr.push(obj)
  35.     }
  36.     console.time("setArray")
  37.     sheet.setArray(0, 0, arr)
  38.     console.timeEnd("setArray")
  39. })
复制代码


适用场景

setDataSource并非在所有情况下都可以代替setArray,它需要满足一定的条件:
1. 需要是明细列表类的数据
举个简单的例子,以下图为例,红框中的数据结构就不适合使用setDataSource来优化性能,而蓝框中的数据则非常适合。

2. 数据量要比较大
在单元格数量超过5000的时候,setDataSource才能体现出比较明显的优势(相比setArray,节约一半的时间),且数据量越大,效果越好

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

0 个回复

您需要登录后才可以回帖 登录 | 立即注册
返回顶部