图表在中后台系统中是一个非常常见的可视化表现形式,也使得数据变得更为的直观。这一小节,我们将通过使用图表类库 G2 来制作一个图表。

备注:阿里提供了 BizCharts 对 G2 进行了封装,在实际开发中可以很方便的进行使用。本小节会从基本的 G2 应用开始,让你体验一下组件开发需要涉及到哪些细节。

引入依赖

G2 需要额外引入,让我们先安装一下吧:

  1. cnpm install @antv/g2 --save

创建图表组件

接着,我们新建一个图表组件于 src/components/SampleChart.js

  1. import React from 'react';
  2. import G2 from '@antv/g2';
  3. class SampleChart extends React.Component {
  4. constructor(props) {
  5. super(props);
  6. this.containerRef = React.createRef();
  7. }
  8. render() {
  9. return (
  10. <div ref={this.containerRef} />
  11. );
  12. }
  13. }
  14. export default SampleChart;

此处我们看到了一个新的属性 ref,通过该属性我们可以获取经过 render 后的真实节点的引用。如果 ref 的节点是一个 dom 元素,那么你得到的是文档中真实的 dom 节点,如果 ref 的节点是一个 component,那么你获得将是该 component 渲染后的实例。而在这里,我们获取的是 div 的 dom。

其中 React.createRef 是 React 提供的一个创建引用的便捷方法。

在获取了 dom 元素后,我们便可以对其进行图表的初始化了。

  1. class SampleChart extends React.Component {
  2. componentDidMount() {
  3. // G2 初始化图形代码
  4. this.chart = new G2.Chart({
  5. // this.containerRef.current 即为引用
  6. container: this.containerRef.current,
  7. width: 450,
  8. height: 300
  9. });
  10. // 下文会介绍
  11. this.refreshChart();
  12. }
  13. // ...
  14. }

componentDidMount 是 React 组件生命周期方法之一,在组件被添加到真实文档后触发。因而我们在这时,ref 可以拿到当前真实的 dom 元素。

这不仅仅适用于 G2,同样适用于那些依赖于真实 dom 元素的库(例如 jQuery)。之后,我们实现数值渲染:

  1. class SampleChart extends React.Component {
  2. refreshChart = () => {
  3. // 接收 data 属性作为数据源
  4. this.chart.source(this.props.data);
  5. // 此处为硬编码,配置源自 G2 官方示例: https://github.com/antvis/g2
  6. // 实际开发中需要封装,推荐直接使用 BizCharts。
  7. this.chart.interval().position('genre*sold').color('genre');
  8. this.chart.render();
  9. };
  10. // ...
  11. }

一个基本的图表组件完成了,但是还有一些小细节还需要考虑。本小节最后会将其补充完成,不过也很欢迎你先想想还漏了哪些东西。

使用图表

同样的,我们需要在 model 和 mock 中加入对应的数据获取步骤。返回的数据格式如下(这是 G2 官方样例数据):

  1. {
  2. result: [
  3. { genre: 'Sports', sold: 275 },
  4. { genre: 'Strategy', sold: 1150 },
  5. { genre: 'Action', sold: 120 },
  6. { genre: 'Shooter', sold: 350 },
  7. { genre: 'Other', sold: 150 },
  8. ]
  9. }

具体的代码可以详见 src/model/cards.jssrc/service/cards.jsmock/cards.js 中的内容。在之前章节已经介绍过它们的内容因而略过。

回到我们上一节开发的 list 页面,我们更新一下 state 添加相关状态:

  1. state = {
  2. // ...
  3. statisticVisible: false,
  4. id: null,
  5. };

然后,我们为表格添加额外的一列用于点击展示图表:

  1. columns = [
  2. // ...
  3. {
  4. title: '',
  5. dataIndex: '_',
  6. render: (_, { id }) => {
  7. return (
  8. <Button onClick={() => { this.showStatistic(id); }}>图表</Button>
  9. );
  10. },
  11. },
  12. ];

添加展示逻辑。

  1. class List extends React.Component {
  2. showStatistic = (id) => {
  3. this.props.dispatch({
  4. type: 'cards/getStatistic',
  5. payload: id,
  6. });
  7. // 更新 state,弹出包含图表的对话框
  8. this.setState({ id, statisticVisible: true });
  9. };
  10. handleStatisticCancel = () => {
  11. this.setState({
  12. statisticVisible: false,
  13. });
  14. }
  15. // ...
  16. }

增加表格的对话框,将我们做好的组件加进去:

  1. render() {
  2. const { /* ... */ statisticVisible, id } = this.state;
  3. const { /* ... */ statistic } = this.props;
  4. return (
  5. <div>
  6. {/* ... */}
  7. <Modal visible={statisticVisible} footer={null} onCancel={this.handleStatisticCancel}>
  8. <SampleChart data={statistic[id]} />
  9. </Modal>
  10. </div>
  11. );
  12. }

好了,现在点击“图表”按钮便可以欣赏到你的图表啦。

图表 - 图1

更多思考

我们回到之前设计的 SampleChart,想想还缺少些什么。

首先,我们现在的逻辑只接收初始化时获取的 data 画图,如果 data 更新图表并不会更新。因而,我们需要监听组件更新事件,更新时重新画图。

  1. componentDidUpdate() {
  2. this.refreshChart();
  3. }

考虑另外一点,如果当前的 data 没有变化我们图表当然不需要重新绘制。因而,添加一个检查只有 data 更新时才重绘:

  1. componentDidUpdate(prevProps) {
  2. if (prevProps.data !== this.props.data) {
  3. this.refreshChart();
  4. }
  5. }

接着,还有一个问题。如果这个组件不再被使用,那么初始化的图表也应该随着组件一并被销毁。所以,我们还需要监听一下卸载事件:

  1. componentWillUnmount() {
  2. if (this.chart) {
  3. this.chart.destroy();
  4. }
  5. }

这样,图表的生命周期就与组件的生命同步了。

最后一点,就是该图表的耦合性过高。我们还需要将一些耦合的部分拆分出来。你可以参考 BizCharts 的实现作为课外阅读。