苦Echarts久已,替之可乎?

6,390 阅读9分钟

背景介绍

🏋🏻‍♂️ 天下苦Echarts久已,替之可乎?

由于工作内容主要涉及到数据可视化方向,所以使用 Echarts 的场景特别多,良好的社区文档、较低的学习成本、人性化的配置项等等,没错这些都是 Echarts 的优点,甚至再看了很多可视化对比文章中,”上手容易“都是列举 Echarts 优点的首选项。

但是,随着大量且深入的使用 Echarts 的过程中,Echarts 这个可视化库暴露出的问题也愈发的明显和致命,而其中最让我难以接受的就是性能

由于大屏展示中涉及到大量的动画,甚至有些会使用逐帧和 worker 绘制离线 canvas 这种操作,而动画对性能的要求又极为苛刻,所以 Echarts 再有大量动画渲染的场景下越来越难以胜任,于是寻找一款有足够动力来替换掉 Echarts 的可视化库变得迫在眉睫。

群雄逐鹿的可视化

目前可视化库简直是百家争鸣,不过大家最熟悉的应该就是国内的Echarts和G2/G2Plot,先来概括一下我了解和使用过的库的一些感受吧(列举不全,也都是一家之言,仅作为参考,不喜勿喷)。

D3

作为可视化库中的王者,D3 的强大是毋庸置疑的,但也正因为它的强大,让人总会产生一种敬而远之的疏离感,换句话说就是 D3 库的使用并不友好,至少在我这个用惯了 Echarts 这种无脑配置的库的人来讲D3的上手成本有点高,它可以做任何事情,但我的需求并不是做出任何样式的可视化。

所以D3在我看来属于特种级别,当现有的可视化库实在无法满足需求,并且手动实现又过于复杂的时候,D3 便是那个托底存在的PlanB。

HighCharts

相较于 D3,显然 HighCharts 更容易上手,但除了这点之外全面被 D3 吊打,另外商业用途需要授权这点上,HighCharts 应该已经被国内大多数团队排除在外了吧。

除了这两个可视化库之外,我了解的就只剩下 Echarts 和阿里的 G2 了,现在重点来对比一下这两个库。

对比 Echarts 和 G2

学习成本、上手难度甚至社区文档这些无疑是 Echarts 完胜,这点毋庸置疑,但是我切换的初衷是Echarts的性能瓶颈已经越来越满足不了我s面对的需求,所以这些 Echarts 的绝对优势所占的比重并不是很高。

声明:测试使用的版本为 Echarts(5.3.0) 、G2(4.1.47)

初始样式

echarts初始化.jpg

Echarts初始化的柱状图

// **Echarts option**
{
  tooltip: {
    trigger: 'axis'
  },
  xAxis: {
    type: 'category'
  yAxis: {
    type: 'value'
  },
  series: [
    {
      data: data,
      type: 'bar'
    }
  ]
}

G2初始化.jpg

G2初始化的柱状图

// **G2 code**
const chart = new Chart({
  container: this.$refs.container,
  autoFit: true
})
chart.data(data)

chart.scale('sales', {
  nice: true
})

chart.tooltip({
  showMarkers: false
})
chart.interaction('active-region')
chart.interval().position('type*sales')
chart.render()

通过代码可以看到,两个可视化都是相同数据下最基础的配置项,但是再不添加其他样式配置的情况下,G2 是默认撑满整个父元素宽高的,而 Echarts 是有间距的,关于这一点 Echarts 官方文档的配置项中也有详细的默认值作为参考。

echartsgrid.jpg

可以看到 Echarts 的grid默认值是上下间距60,左右间距10%,而当把这些配置项调整为 0 时 Echarts 就会变成这样。

echarts0.jpg

这时候有 Echarts 用的熟的同学就会跳出来说,你不配置 containLabel 却在这吹毛求疵,这不是耍流氓吗?

grid. containLabel: 区域是否包含坐标轴的刻度标签,默认为false,即不包含。

那咱就配置上再看下效果。

grid: {
  top: 0,
  bottom: 0,
  left: 0,
  right: 0,
  containLabel: true
}

echartstrue.jpg

可以看到,Y轴Label溢出了。可能有同学会说这个时候为了防止溢出就不应该都设置为 0 啊,是的,没错我可以调整间隙让这个图表看上去正常且美观,但是你也不得不承认 Echarts 在这个场景下失控了。

如果要将设计稿上的可视化图表以像素级别还原的话, Echarts 就变得没那么好用了,不是吗?

当然,这只能算是 Echarts 的小问题,如果不是纠偏那么严格的情况下,可以说”无伤大雅“,那接下来让我们看一个于我而言更重要的指标。

性能表现

还是以这个最基础的柱状图为例,通过Chrome的Performance我们可以查看到底层函数的渲染时间如下。

echarts性能.jpg

Echarats的渲染耗时

g2.jpg

G2的渲染耗时

同样的数据相同的图表,Echarts 渲染耗时82.3ms,G2 耗时40.5ms,Echarts的性能消耗几乎是G2的2倍之多,性能优劣简直一目了然。说真的,测试之前我只是相信G2的性能会更好,但没有想到差距竟然如此夸张。

当然,我也顺手测试了一下 G2Plot 的性能表现。

g2plot.jpg

G2Plot的渲染耗时

几乎与 Echarts 相差无几,看来 G2Plot 经过了一层翻译之后性能表现上确实远不如直接调用 G2,所以如果只是从 Echarts 切换到 G2Plot 的动力并不是很大。

可能有同学会觉得,虽然相差一倍的性能表现,但是仅仅 40ms 的差异就舍弃 Echarts 的易用性有点夸张了。对此我只能说我们遇到的场景不同,如果你不追求一个极致或者满足流畅和帧率的动画,那么几十毫秒的差距确实可以忽略不计,但是对于上述的场景而言,几十毫秒的性能提升已经足够的立竿见影了,更何况 Echarts 还会随着配置项的增多,data数量的庞大导致性能越发的不堪。

复杂图表的性能表现

先来看一下稍微复杂一点的带有动画的 Echarts 使用场景。

echarts动图 (1).gif

带有动画效果的Echarts

这是 Echarts 动画最后阶段渲染全部数据时的渲染耗时。

echarts动画性能.jpg

Echarts渲染耗时

用G2简单实现了一下(请忽略背景的展开动画,实在懒得完全还原了,因为纯CSS的动画不会影响本次测试)

G2的副本.gif

带动画效果的G2实现

G2动画性能.jpg

G2的渲染耗时

随着数据的分步渲染和变得复杂的配置项, Echarts 的渲染性能已经进一步的下降,可以看到最后一次渲染全数据时的耗时已经达到 473.8ms,而 G2 仅为 168ms,两者相差近 3倍,这时性能的差距不再是不痛不痒的 40ms,而是达到了恐怖的 310ms,我相信如果我切换到地图的渲染这个结果会更加夸张。

性能测试到这里,已经没有必要深究下去了, Echarts 在渲染消耗方面基本被 G2 碾压,当然只有这一个理由的话,还不足以坚定我直接切换可视化底层的决心,毕竟所有的可视化组件重构一遍是一个不小的工程了,但是在阅读文档和写测试Demo的过程中,逐渐又发现了一些其他对比。

不再是预估值的splitNumber

echarts文档.jpg

Echarts 官方文档上对于splitNumber的描述

不知道大家有没有遇到过苦苦跟设计师解释为什么坐标轴上的刻度个数与设计稿不符,再解释的过程中要不断强调这是个预估值我控制不了啊。

没错,官方文档已经很清楚的描述了,这是个预估值,也就是说这个配置项和最终渲染是会有一定差异,所以看出来了吗, Echarts 再次变得失控了,当然最后那句”类目轴中无效“实际上更坑。

那让我们来看看 G2 文档中对应配置的描述,

tickCount.jpg

G2文档中的描述

看到了吗,不再是预估值,而是一个精确的个数,你可以精准的掌控坐标轴上刻度个数,经过测试也确实如此,除了 tickInterval 可以影响它之外,一切都在你的掌控之下。

ScaleOption.tickInterval: tick 间隔,只对分类型和时间型适用,优先级高于 tickCount。

Label不再互相遮挡的饼图

不知道大家有没有遇到过饼图展示Top数据时,有那么1~2笔的数据占比过于巨大,导致后面占比小的数据甚至需要设置最小的角度才能显示出来,这时候如果再加上引导线和 label 的话,无疑是对可视化库算法的一次大考,现在我们就来对比一下 Echarts 和 G2 在这个场景下的表现。

echarts.jpg

Echarts对于占比失衡数据的Label展示

G2pie.jpg

G2对于占比失衡数据的Label展示

可以看到 G2 的展示要远远优于 Echarts ,虽然 G2 是通过调整label的配置才能显示成这样,但即使我设置 Echarts 的 labelLine 属性中的 length、length2 和 minTurnAngle,效果依旧不佳,再次感慨一句 Echarts 又失控了。

值得一说的是,Echarts5.0+ 已经对饼图Label的算法有很大改善了,如果是 Echarts4.0+,可能就是下面的这种效果了。

echarts4.0.jpg

Echarts对于饼图的渲染

G2饼图.jpg

G2渲染的饼图

总结

首先要承认这篇文章乃至这次的测试,我主观意向上本身就是偏向于 G2 的,有这层私心主要是因为过去使用 Echarts 的过程中发生了太多不那么愉快的状况,而我对 G2 还属于摸索学习的阶段,所以这篇文章可能在某些观点上有失偏颇,但是测试的结果是没有掺杂个人因素的,大家可以当做参考。

这里需要声明一下,并不是推荐大家放弃 Echarts 而去使用 G2,毕竟 G2 明显没有 Echarts 那么易用,我切换的原因仅仅是因为 Echarts 愈发无法满足我遇到的需求而已。

虽然 G2 也有很多缺点,简单到粗糙的文档、命令式声明的繁杂、多出的很多概念,甚至修改坐标轴label 的颜色要使用 fill 这个属性,这些都是槽点,但是它优秀的性能和掌控性恰恰是我现在所热衷的。

随着工作年数的增长,越来越体会到代码的可控性有多重要,一个轮子可以是黑盒,这没问题,但是如果有一天你对这个黑盒失去了掌控,那么就应该去换一个黑盒,如果还是不能满足也可以试着去做一个符合你预期和需求的完全属于你的黑盒,以上。

🧞‍ G2 还在摸索使用当中,如果后续发现 Echarts 和 G2 更多的槽点,或者 Echarts 优于 G2 的地方,都会在这篇文章后面继续更新。