Hi~ 我是前端学徒业枫,小伙伴们有看前几天的云栖大会直播吗?我已经被“无影”刷屏了...云栖除了云电脑,还有大量的大屏可在线上直接体验,那么在阿里我们是用什么样的组件开发大屏的呢?
我将向大家分享可视化系列文章,介绍我们的前端开源图表库📈 CloudCharts(Github) 如何基于 G2 封装,通过少量配置项即可绘制图表,最终服务内部近两百个产品和许多对外云产品。
我将针对 G2 在大规模使用场景中遇到的问题做详解:
-
组件库整体的架构是什么?如何在React中优雅的使用,同时管理好组件内部的代码?
-
如何统一约束数据格式?
-
如何做到开箱即用,降低使用门槛?
-
如何收敛图表主题展现并快速切换主题?
-
如何低代码构建图表?
今天是系列第一篇,来讲讲前两个问题~
问题分解
1、组件库整体的架构是什么?如何在React中优雅的使用,同时管理好组件内部的代码?
在 React 中使用 G2 有 BizCharts 和 Viser 等方案,但他们只是做了简单的语法糖,距离真正开箱即用还有一段距离。因此我们要制定一套方案,既能让用户使用简单,又能管理好组件库内部的代码,保证可维护性。
2、如何统一约束数据格式?
G2 的数据映射十分灵活强大,但在实际业务使用中反而会增加学习门槛,降低效率。所以我们将组件的数据格式统一约束为类似 HighCharts 的数据格式,在内部做数据映射。
有了思路,接下来康康具体怎么实现👀 :
1、建立生命周期
首先搭建组件库整体的架构,让项目能长期的维护和发展,尽量减少可能产生的历史包袱。使我们的组件库能在React 中优雅使用,并管理好组件内部代码。
一开始没有好的思路不要紧,我们先把组件绘制图表的全部流程列出来:
观察图表绘制的过程,我们可以将其按照组件生命周期划分为几个部分:创建期、存在期、销毁期。这和 React 的生命周期是基本一致的。
我们发现这些绘制流程很大一部分是可以复用的,大多数组件只有“声明绘制逻辑”这一步不一样,以此建立一套生命周期管理机制。
组件代码由原生代码实现,负责声明 G2 的图形语法。中间的工厂函数负责创建图表实例,调用生命周期,以及一些复用的逻辑,最后返回 React 组件。
工厂函数示例
工厂函数是管理生命周期的核心,在这里简单展示工厂函数的代码。实际逻辑会多很多,有兴趣的同学可以去 Github 查看源码:传送门
import G2 from '@antv/g2';
import React from 'react';
function factory(name, component) {
class CloudCharts extends React.Component {
componentDidMount() {
// 设置初始高宽
this.initSize();
// 初始化图表
this.initChart(this.props);
}
componentDidUpdate(prevProps) {
// 配置项有变化,重新生成图表
this.rerender();
// 数据有变化,更新数据
component.changeData(data);
}
componentWillUnmount() {
this.destroy();
}
// 初始化适配高宽
initSize(props) {}
// 初始化图表
initChart(props) {
this.chart = new G2.Chart({
container: this.chartDom,
});
// 调用组件代码
component.init(this.chart);
}
destroy() {
component.destroy();
}
render() {
return (
<div ref={dom => (this.chartDom = dom)} />
);
}
}
CloudCharts.displayName = `CloudCharts${name}`;
return CloudCharts;
}
2、数据格式映射
建立起统一的生命周期管理后,下一步要约束用户传入的数据格式。我们在工厂函数中内置一层数据转换,允许用户传入 HighCharts 格式的数据。那么内部是怎么实现的呢?
我们来观察一下这张普通的图表,只由一条折线和点组成:
对应的 G2 数据格式是一个非常简单的 JSON 数组,数组中的每一项对应图表的一个数据点:
const data = [
{ year: '1995', value: 4.9 },
{ year: '1996', value: 6 },
{ year: '1997', value: 7 },
{ year: '1998', value: 9 },
{ year: '1999', value: 13 },
];
传入 G2 后需要指定数据映射(data fields):year * value
而Highcharts格式稍微复杂一些,但是关键内容(数据的数值)都是一致的:
const data = [
{
// name 用于区分多组数据
name: '折线一',
data: [
['1995', 5],
['1996', 6],
['1997', 7],
['1998', 9],
['1999', 13],
],
},
];
所以可以通过循环处理转化为G2的格式:
const newData = [];
data.forEach(oneData => {
const { name: dataName } = oneData;
oneData.data.forEach((d, i) => {
const [x, y, ...extra] = d;
newData.push({
x,
y,
extra,
type: dataName,
});
});
});
这里返回的 x
、y
、type
对应了图形语法中的 field
:
chart.geom().position(['x', 'y']).color('type')
等于图表库内部固定了 data fields,用户可以使用 HighCharts 格式的数据,这也方便了之前使用 HighCharts 的业务迁移到新图表库中。
中场休息的分割线
有了上面的两步,整个图表库的框架已经有了雏形。
本期就到这啦,下回继续分解!
📢 📢 大家对此有什么想法呢?欢迎在评论提出~
✏️ 下期预告
我会着重解答第三个问题
「如何做到开箱即用,降低使用门槛」
原文首发于微信公众号,记得持续关注我们哦~