编者按:本内容源自葡萄城客户——政采云前端技能团队。政采云公司以全球领先的云核算、大数据、人工智能等数字技能为根底,搭建了全国首个政府收购云服务渠道——政采云渠道,现在该渠道已成为职业内服务范围最广、用户数量最多、买卖最活泼的跨区域、跨层级、跨领域的一体化收购云服务渠道。
前言
数据可视化包含三个分支:科学可视化、信息可视化、可视剖析。
1、科学可视化首要重视的是三维现象的可视化,如建筑学、气象学、医学或生物学方面的各种体系。要点在于对体、面以及光源等等的传神烘托,或许乃至还包含某种动态成分。
2、信息可视化是一种将数据与规划结合起来的图片,有利于个人或安排简略有用地向受众传播信息的数据表现形式。
3、可视剖析学被定义为由可视交互界面为根底的剖析推理科学,将图形学、数据挖掘、人机交互等技能融合在一起,构成人脑智能和机器智能优势互补和彼此提高。
可视化剖析中可视化报表是重中之重,把许多的数据快速的展现出来,而且灵敏的进行数据操作,其间操作包含数据的挑选、相关、联动、钻取,案牍的查询,替换、款式设置,条件格局的注入完结多色阶、图标集、数据条、重复值,以及公式的插入,跨表联动等。SpreadJS 在处理可视化剖析报表中最为杰出,下面咱们只针对可视化剖析中 SpreadJS 所扮演色做讨论。
报表可视化难点
互联网电商服务业职业,平常会处理许多商业信息和用户信息,客服和数据剖析师,是报表首要用户人员。
客服平常每天都会处理许多的工单填报、客诉登记、第三方渠道原始数据的导入、核算汇总、审核审批、电签、分发等作业。平常大部分作业信息的载体都是 Excel,每天服务器需求处理海量的文档,因为 Excel 文档自身数据难以提取入库,模板更新时也不方便第一时刻分发到操作员处,难以整合到 Web 页面里等问题。
数据剖析师需求拿到数据进行汇总,算出各个商品品牌的销售额,最大值、最小值、平均值等,标识出有价值的数据。抓取有用数据,制作成报表给 boss。
针对以上的场景,报表可视化能够总结出以下几个难点:
1. 并发
公司客服人数众多,几千人一起在线重度操作,事务流转周期短、数据量大,所以对服务端并发功用消耗是很大的。能够在后台用 Apache POI 来提取和修正 Excel 数据、并履行其间的公式核算等。这样会遇到两个功用瓶颈:
1)需求频频地上传、下载文档,服务器带宽接受了很大的压力;
2)一切 Excel 解析、提取的操作都在服务器端,频频的 IO 操作让服务器不堪重负。
以上两个功用点,在现在的架构下很难突破,这也是重构项目时最具挑战性的需求点之一。当然硬堆服务器配置也是一个处理方案,但无法处理其它的一些问题,而且也会带来运维的压力。
2. 对 Excel 操作和兼容性要求较高
新体系如果不能让咱们快速上手运用,以这个项目用户的体量,训练成本将无法接受。而且要能够直接导入已有的 Excel 报表模板,不然再次开发或规划一切 Excel 报表也是难以接受的。
3. 报表格局灵敏多变
针对不同的事务场景,报表的模版也是千变万化。因此不需求研发的介入,操作员的规划和填报都能够在页面上完结显得尤为重要。
4. 支撑公式核算
因为涉及到商品、订单、成本核算、财政核算等模块,对核算公式的种类和功用要求较高。
5. 作业流中的数据文档
曾经体系的作业流,涉及到 Excel 报表时,要么数据会先在服务端和 Excel 模板进行组装,要么体系依据路径找到文件服务器的 Excel 文件,然后流转到对应环节。一些新的事务模块,乃至还只能用邮件进行文件传输。
这个过程会产生许多的文件,对文件服务器的带来了很大压力,后台也不得不定期做批量的数据拆分和保护。这次升级体系需求处理这个问题。
思考怎样选型
首要,选型的第一步便是搞清楚市面上详细有哪些产品供咱们选择,关于现在市面上能集成到体系中,支撑这种在线表格文档修改的产品有不少,大体我把他们分了两类。
1. 云文档类型产品
这种产品有许多,类似 WPS、石墨文档、office online。它们自身具备较高的完结度,现已帮用户完结了包含在线协同内的简直一切功用,乃至也支撑必定程度的二次开发而且能够私有化布置。但问题在于通常这类产品封闭性比较强,二次定制开发还是相对比较困难,且不够轻量。授权方式也多以按时刻、按并发量、用户数量等方式授权,价格昂贵,不是很合适咱们的需求。
2. 控件类型产品
像 LuckySheet、Handsontable、SpreadJS 这种便是规范的控件了,它们都是纯前端表格控件,都支撑 Excel 的功用特性和 json 数据绑定。
LuckySheet 是国内的MIT开源软件,能够拿来商用。但在我调研时它才刚上线 1、2 个月,而且不像 React 这种有某个大厂来背书,所以不或许拿来用到咱们的正式项目里。截止现在现已过去了 1 年,接连推出了 QQ 群、论坛等交流渠道,但仍显薄弱。
Handsontable 是国外的一个商业表格控件,据说二次开发坑较多,但对咱们来说最大的问题是它没有中文支撑团队。
SpreadJS 是葡萄城公司的商业Excel表格控件,有趣的是我发现在 V2EX 的 LuckySheet 下方谈论区中,LuckySheet 的作者也说 SpreadJS 是职业标杆。它支撑导入包含公式、图表、款式、条件格局在内的绝大部分 Excel 特性(不支撑宏)。而且最惊喜的是,它的操作界面是一个完整的 Excel 界面,完全纯 JS 开发的,用 json 进行模板和数据交互。一起 SpreadJS 也有对应的售后支撑团队,技能问题能够作业日期间随时电话、论坛交流,相关的材料包含视频、文档、示例、API 手册也都非常丰富,乃至还能够请他们的技能顾问来公司训练。关于像咱们这种工期短、开发使命比较繁重的项目组,的确能节省许多的精力,降低了风险。
图片来历:SpreadJS在线Excel修改器
那么什么是控件?为什么要用控件?
引证维基百科
在核算机编程傍边,控件(或部件,widget或control)是一种图形用户界面元素,其显现的信息排列可由用户改动,例如视窗或文本框。控件定义的特点是为给定数据的直接操作(direct manipulation)供给独自的互动点。控件是一种根本的可视构件块,包含在运用程序中,操控着该程序处理的一切数据以及关于这些数据的交互操作。
依照我自己了解,控件便是只供给了根本功用,支撑二次开发的功用模块。控件相对依赖更轻,可塑性更好,而且也有对应的开发文档和 API,是面向开发者的根底功用包,便于按需求来定制功用。
SpreadJS 需求处理方案和优势
1. 并发
因为 SpreadJS 是数据和模板分离的规划,填报人员只需求在页面上完结填报。提交时能够只提交填报好的数据 json 即可,服务器再也不必会集解析一切Excel 文件了。带宽消耗也直接节省了一半。
2. 对 Excel 操作和兼容性要求较高
在内部试用时,财政和客服的小姐姐们反应,运用体验跟 Excel 简直完全相同,不需求再特意训练。而且咱们自己的许多 Excel 报表能够直接导入进去(二次开发后也能够完结批量和远程导入),包含图表、公式、表格款式等等一系列元素都能够直接导入线上操作。
3. 报表格局灵敏多变
规划人员能够直接在线规划,或许把 Excel 规划好的报表,拿到 Web 端,做好数据绑定,提交保存成 json 格局即可(Spread JS 的 ssjson 格局包含 Excel 文档的一切信息)
4. 支撑公式核算
支撑了 450 多种( Excel 总共 480 多种)公式,还能够自己开发扩展自定义公式,对财政也够用了。一起还支撑一切 Excel 的引证操作,比如跨 sheet 引证、必定引证、函数命名信息之类。
5. 作业流中的数据文档
根本脱离了对文件的依赖,一切流程状况和依赖的数据都能够在数据库中记载,文件服务器只需求保存少量的模板文档即可(其实模板数量不大时能够直接放到数据库里,不过咱们有现成的文件服务器)。这里节省了咱们 90% 文件服务器的空间开支,运维的小伙伴深夜都要笑醒。
深化SpreadJS
要点来了,其实最让我这个前端开发者感兴趣的便是 SpreadJS 的一些底层规划、以及对内存、功用平衡性的优化。对此我做了许多调研和学习,好在这方面材料不难找,常常能够在葡萄城官方论坛的公开课版块( gcdn.grapecity.com.cn/forum.php?m…
烘托功用
功用必定是每个深度表格控件用户最忧虑的问题。咱们的数据量常常到达好几千条,而且 Excel 不方便分页(涉及前端的公式核算汇总),所以选型期间很忧虑。后来发现想多了,SpreadJS 能够轻松加载 50 万条数据加载耗时 200 ms左右(官网功用演示示例只能加载 5 万,咱们自己扒下来测的 50 万)。后来深化了解才知道,处理这个问题,他们的思路是这样的:
- 实时烘托 + Double buffering (翻译成双层缓存?):
用 Canvas 烘托表格部分,而且只烘托用户看到的部分内容,这就完结了加载 1000 行和加载 100000 行数据速度都很快,功用相差不大的现象。
而 Double buffering 是为了处理接连烘托的接连性体验问题,也能够进一步提高烘托速度。这个名词估量听过的人少,但应该人人都体验过,Double buffering 在图形学里,一般称作双缓冲,实际上的绘图指令是在一个缓冲区完结,这里的绘图非常的快,在绘图指令完结之后,再通过交换指令把完结的图形当即显现在屏幕上,这就避免了呈现绘图的不完整,一起效率很高。在游戏里其实很常见,当咱们主控的人物在地图上奔驰时,游戏引擎会依照人物移动方向实时加载和烘托地图,这就避免了一次性加载超大地图时那漫长的等待。
图片来历:葡萄城公开课【SpreadJS功用优化】
(gcdn.grapecity.com.cn/forum.php?m…
SpreadJS 功用优化 – 葡萄城公开课 – 葡萄城产品技能社区 (grapecity.com.cn)
- 稀少数组:
SpreadJS 对表格数据的存储优化采用了稀少数组的数据结构。稀少数组常用来优化二维数组(比如棋盘、地图等场景)的内存占用,但它有个天然生成的缺陷,便是访问功用慢。
所以其时针对这个疑问,我给它做了压力测验,百万级别的遍历耗时 200 多ms。功用能够满足咱们的需求。
核算引擎
据官方介绍来看,公式引擎其实是包含了两大完结的部分,一个是核算逻辑体系、一个是引证体系。
- 引证体系
Excel中公式的核算都是依赖于某些原始数据的,比如 C1 引证 B1、B1 又引证 A1等等, SpreadJS 把这部分功用封装的现已非常原生化了,根本不需求开发者操心(除非有引证回溯等特别需求)。
Excel 中 有直接引证、跨 Sheet 表单引证、相对/必定引证、命名信息的引证、 table 队伍公式的引证、跨作业簿引证等等(没列举完,感兴趣的同学能够自行搜索学习)。SpreadJS 的 runtime 是在网页端,因此跨 Workbook 引证就别想了,至少现在必定没支撑。
- 核算逻辑
SUM、IF、MATCH、VLOOKUP 这种能输入到单元格里的核算公式,用起来就像是一个个的小“逻辑包”,现在 SpreadJS 有 460+ 种原生的公式函数,而 Excel 只有 490+ 种,而且 SpreadJS 能自定制公式,运用体验与原生公式相同。
关于底层完结,实际上通过多个版别的迭代,这些公式早已不是一个个独立的“逻辑孤岛”了。公式的完结在底层有许多的抽象和复用,据说新版别在功用提高的一起,代码量比老版别有显着精简,这对前端工程打包也是比较友爱的。
关于嵌套公式核算的完结,SpreadJS 在底层树立起了 AST 树来解析用户设置公式的核算逻辑,从官方示例的代码来看,公式底层树立了一套 Expression,而且有对应的 public 接口可供调用,如图:
图片来历:【SpreadJS公式结构树形展现】
gcdn.grapecity.com.cn/showtopic-7…
- 功用
首要,作为一个前端技能,咱们能够先从公式核算的技能要求上来剖析功用或许呈现的瓶颈以及形成的影响。咱们在开发时用到了许多的用户事件、脏数据、联动等功用,一切这些功用保证正确运转的一个重要前提,便是有必要能保证随时能够拿到正确的核算成果,那么最直接的完结思路便是让公式以高优先级、同步的方式来履行完核算。
咱们都知道,多线程能够协助分担核算压力,但是先抛开规划和完结难度不说,即便支撑了 Web Worker,JavaScript 严格来说也只能算是一个单线程言语,因为它的 Web Worker 子线程完全受主线程操控,而且主线程无法被堵塞挂起。所以即便引入了 Web Worker,也无法保证上边提到的同步履行。
通过以上剖析,能够看出公式核算功用的局限性,取决于 JavaScript 的核算能力。我找了一张相关的图片,能够直观反映 Node.js 的核算能力(Node.js 是 V8 引擎,公认最快的 JS 引擎)
图片引证自《深化浅出Node.js》
据咱们测验,以上核算功用接近原生JS的核算功用,SpreadJS 在这方面的优化现已非常接近物理极限了。现在在咱们的运用场景中,这个核算功用现已足够运用,但不扫除今后会呈现海量的数据和公式的核算需求,而在这方面官方也给出了相关处理方案,参阅这里。
据说,官方还在进一步开发缓存技能,来完结公式核算的分块缓存:即便引证链上有值发生变化,也不需求核算整个引证链的公式。听起来很强大,思路也靠谱,期望早点推出。
款式体系
Excel 的款式体系非常复杂,边框、字体、对齐、数据格局、条件格局等等每一个功用点都有非常灵敏庞大的完结,刚开始了解 SpreadJS 时,我也被它的 Style 类惊呆了,除了我能幻想到的边框、布景、字体、对齐等这些能“看得到”的,竟然也有单元格类型、数据格局、表格按钮、下拉、水印这类东西。不由得感叹 Style 太重了,如果定制许多的单元格款式,内存和功用必定都不好。不过实际运用中倒也没发现瓶颈,本来这里采用了分层结构来规划,如图:
图片来历:葡萄城公开课【SpreadJS功用优化】
SpreadJS怎样用?
1. 烘托表格
图 6.1-1 绑定数据和公式
首要获取大局 spread 目标,spread 是整个表格的主体,spread 又分成多个 sheet。SpreadJS 初始化结束都会返回一个 spread 目标。
- vue 版别 spread 目标
<gc-spread-sheets @workbookInitialized='spreadInitHandle(\$event)' />
methods:{
spreadInitHandle: function (spread) {
this.spread = sprea
},
}
-
绑定数据,绑定公式
tableDataBind() { // 数据源,能够从后台恳求拿到 var dataSource = { // 留意这里加了一层bindPath,用于映射表格的绑定路径 bindPath_table: [{ c1: 100, c2: 90, c3: 30, c4: 40 }, { c1: 88, c2: 66, c3: 55, c4:100 }, { c1: 30, c2: 89, c3: 100, c4: 40 },{ c1: 40, c2: 66, c3: 88, c4: 40 }] }; // 表格绑定和单元格绑定数据源,需求用SpreadJS的CellBindingSource包装一下 var spreadNS = GC.Spread.Sheets; var dataSource1 = new spreadNS.Bindings.CellBindingSource(dataSource); var table2 = this.activeSheet.tables.add("tableName", 0, 0, 1, 5, spreadNS.Tables.TableThemes.light6); table2.showFooter(true); table2.autoGenerateColumns(false); var c1 = new spreadNS.Tables.TableColumn(1); c1.name("语文"); c1.dataField("c1"); var c2 = new spreadNS.Tables.TableColumn(2); c2.name("数学"); c2.dataField("c2"); var c3 = new spreadNS.Tables.TableColumn(3); c3.name("英语"); c3.dataField("c3"); var c4 = new spreadNS.Tables.TableColumn(4); c4.name("化学"); c4.dataField("c4"); var c5 = new spreadNS.Tables.TableColumn(5); c5.name("合计"); table2.bindColumns([c1, c2, c3, c4, c5]); table2.bindingPath("bindPath_table"); // 设置公式 table2.setColumnDataFormula(4, "=[@语文]+[@数学]+[@英语]+[@化学]"); table2.setColumnFormula(4, "=SUBTOTAL(109,[合计])"); // 设置允许单元格的内容超出单元格,与绑定无关 this.activeSheet.options.allowCellOverflow = true; // 绑定dataSource this.activeSheet.setDataSource(dataSource1); this.spread.resumePaint(); }
图 6.1-2函数名和函数码映射表
烘托条件格局
烘托条件格局:数据烘托完结只能保证数据能正常显现出来,但是这还不能满足数据剖析师的需求,还要显着展现有用数据譬如:最大值,最小值标红,进度条展现一个变化状况,图标展现上升还是下降,双色阶,三色阶,等,详细怎样完结?
- 图标集:作用如图
-
完结代码
iconset() { var activeSheet = this.activeSheet; var iconSetRule = new GC.Spread.Sheets.ConditionalFormatting.IconSetRule(); // 演示demo先写死区域 iconSetRule.ranges([new GC.Spread.Sheets.Range(0,0, 5, 5)]); // IconSetType图标志的类型:箭头,圆圈和execl 打通的,excel有哪些这这边就支撑哪些 iconSetRule.iconSetType(GC.Spread.Sheets.ConditionalFormatting.IconSetType.threeArrowsColored); var iconCriteria = iconSetRule.iconCriteria(); iconCriteria[0] = new GC.Spread.Sheets.ConditionalFormatting.IconCriterion( true, GC.Spread.Sheets.ConditionalFormatting.IconValueType.number, 60 );(<60) iconCriteria[1] = new GC.Spread.Sheets.ConditionalFormatting.IconCriterion( true, GC.Spread.Sheets.ConditionalFormatting.IconValueType.number, 90 );(60<= <90) iconCriteria[2] = new GC.Spread.Sheets.ConditionalFormatting.IconCriterion( true, GC.Spread.Sheets.ConditionalFormatting.IconValueType.number, 90 );(>=90) iconSetRule.reverseIconOrder(false); iconSetRule.showIconOnly(false); activeSheet.conditionalFormats.addRule(iconSetRule); }
-
进度条:作用如图
-
完结代码
dataBar(){ var activeSheet = this.activeSheet; activeSheet.conditionalFormats.addDataBarRule( GC.Spread.Sheets.ConditionalFormatting.ScaleValueType.number,0,//最小数 GC.Spread.Sheets.ConditionalFormatting.ScaleValueType.number, 100,//最大值 "orange",//色彩 [new GC.Spread.Sheets.Range(0,0, 5, 4)] ); },
-
重复值:作用如图
-
完结代码
duplicateValue() { var activeSheet = this.activeSheet; var style = new GC.Spread.Sheets.Style(); style.backColor = "yellow"; style.foreColor = "red"; var ranges = [new GC.Spread.Sheets.Range(0,0, 5, 4)]; activeSheet.conditionalFormats.addDuplicateRule(style, ranges); } -
包含文本 6 的单元格:作用如图
-
完结代码
includeText() { var activeSheet = this.activeSheet; var style = new GC.Spread.Sheets.Style(); style.backColor = "red"; var ranges = [new GC.Spread.Sheets.Range(0,0, 5, 5)]; activeSheet.conditionalFormats.addSpecificTextRule( GC.Spread.Sheets.ConditionalFormatting.TextComparisonOperators.contains, "6", style, ranges ); }
-
综合以上完结成果如图
写在最终
本文首要介绍了自己在数据可视化方向的一些探索,针对一些准备做商场大盘以及邮件订阅报表,线上协同协作,可视化剖析等方向的同学有必定的协助。
因篇幅较长,所涉及概念性的东西比较多,难免会呈现过错,期望咱们多多指正,谢谢咱们!
============================
小编有话说:感谢政采云前端技能团队对葡萄城产品的认可并供给上述内容。如您也有关于葡萄城产品的运用心得,欢迎向咱们投稿,在微信大众号后台联络咱们即可。