从零搭建antv数据可视化大屏(轻量级vite-react-ts)

604 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

之前Ofter详细介绍过用Vue从零开始编写可视化大屏,今天我们来介绍下如何用React编写可视化图表。为什么我们还要学习React?因为轻量化。

知识点1:React和Vue的区别

ReactVue
组件react主打轻量级,细化了组件功能,就是你需要什么,就去一个个引入(比如要使用链接功能,就需要引入组件)。因此,React需要开发者有一定的前端基础,更适合专业人士搭建轻型系统。vue封装组件功能更强大,即便是初学者,不需要知道很多原理,就可以引入组件使用。因此,当功能越来越多时,Vue引入的组件冗余内容会越来越多。
数据react整体是函数式的思想,把组件设计成纯组件,状态和逻辑通过参数传入,属于单向数据流,数据可视化往往不需要双向数据流。vue的思想是响应式的,也就是基于是数据可变的,通过对每一个属性建立Watcher来监听,当属性变化的时候,响应式的更新对应的虚拟dom。
jsreact的思路是all in js,通过js来生成html,还有通过js来操作css、styled-component、jss等。vue是把html,css,js组合到一起,用各自的处理方式,vue有单文件组件,可以把html、css、js写到一个文件中,html提供了模板引擎来处理。

一、准备工作

编译工具:Pycharm

知识点2:前端构建工具:Vite

新型前端构建工具,能够显著提升前端开发体验。之前Ofter介绍创建vue项目的构建工具是webpack,但是它有一个很大的弊端是打包会随着项目越大越慢,此套可视化库图表比较多,因此安利vite这个前端构建工具。

知识点3:前端编译工具:TypeScript

TypeScript是一个编译到纯JS的有类型定义的JS超集,适合大规模JavaScript应用。TS在团队协作、可维护性、易读性、稳定性(编译期提前暴露bug)等方面上有着明显的好处。

1. 创建项目

1.1 用vite创建react项目

左下角Terminal终端执行以下命令

npm create @vitejs/app

注:之前Ofter跟大家分享的是用webpack来创 建vue项目,webpack有个大弊端,打包会随着项目越大越慢,用vite会快很多,对开发更友好。

1.2 选择react/react-ts

 Project name: ant-visual
 Select a framework: » react
 Select a variant: » react-ts

1.3 启动项目测试

创建完成后的项目结构,我们可以看到比vue的项目结构简单很多。

cd ant_visual //项目名
npm run dev

2. 安装库

此次我们以anvt/g2图表库为例,安装"@antv/g2",若我们还需要使用路由,再安装一个"react-router-dom"

npm install @antv/g2

npm install react-router-dom

3. 新建目录和文件

3.1 新建components目录和tsx文件

(TypeScript JSX File)

3.2 新建assets和style目录

3.3 新建router目录和index.tsx文件

二、图表实现

1. 图表案例

我们先看下antv/g2有哪些图表,除了常用的柱状图、折线图等,还有很多交互图表。

2. 图表代码

Ofter先从简单的条形图开始,完整地演示一遍。

2.1 在components下新建Bar.tsx

同时,我们可以把官网上的代码复制到Bar.tsx中,和vue一样,这个代码没法直接用,需要经过一些调整。

知识点4:图表渲染

vue和react都需要渲染Dom,一般通过

中的id和ref,但是react中的写法与vue差别会有点大。

2.2 引入useRef, useEffect

在官网代码的基础上,我们需要添加以下代码:

将new Chart()中的container内容改为ref.current

完整代码如下:

import { Chart } from '@antv/g2';
import React, {useRef, useEffect} from "react";

const MyChart = () => {
    const ref = useRef(null);
    const data = [
        {city: '中国(北京)', type: '首都人口', value: 0.01},
        {city: '中国(北京)', type: '城市人口', value: 0.53},
        {city: '中国(北京)', type: '农村人口', value: 0.46},
        {city: '美国(华盛顿)', type: '首都人口', value: 0.01},
        {city: '美国(华盛顿)', type: '城市人口', value: 0.8},
        {city: '美国(华盛顿)', type: '农村人口', value: 0.19},
        {city: '印度(德里)', type: '首都人口', value: 0.02},
        {city: '印度(德里)', type: '城市人口', value: 0.3},
        {city: '印度(德里)', type: '农村人口', value: 0.68},
        {city: '俄罗斯(莫斯科)', type: '首都人口', value: 0.08},
        {city: '俄罗斯(莫斯科)', type: '城市人口', value: 0.66},
        {city: '俄罗斯(莫斯科)', type: '农村人口', value: 0.26},
        {city: '法国(巴黎)', type: '首都人口', value: 0.16},
        {city: '法国(巴黎)', type: '城市人口', value: 0.63},
        {city: '法国(巴黎)', type: '农村人口', value: 0.21},
        {city: '韩国(首尔)', type: '首都人口', value: 0.19},
        {city: '韩国(首尔)', type: '城市人口', value: 0.63},
        {city: '韩国(首尔)', type: '农村人口', value: 0.18},
        {city: '丹麦(哥本哈根)', type: '首都人口', value: 0.22},
        {city: '丹麦(哥本哈根)', type: '城市人口', value: 0.65},
        {city: '丹麦(哥本哈根)', type: '农村人口', value: 0.13},
        {city: '冰岛(雷克雅未克)', type: '首都人口', value: 0.56},
        {city: '冰岛(雷克雅未克)', type: '城市人口', value: 0.38},
        {city: '冰岛(雷克雅未克)', type: '农村人口', value: 0.06},
    ];


    useEffect(() => {
        if (!ref) return;
        const chart = new Chart({
            container: ref.current,
            autoFit: true,
            height: 500,
        });
        chart.data(data);
        chart.scale('value', {


            alias: '占比(%)',
        });
        chart.axis('city', {
            tickLine: null,
            line: null,
        });
        chart.axis('value', {
            label: null,
            title: {
                style: {
                    fontSize: 14,
                    fontWeight: 300,
                },
            },
            grid: null,
        });
        chart.legend({
            position: 'top',
        });
        chart.coordinate('rect').transpose();
        chart.tooltip({
            shared: true,
            showMarkers: false,
        });
        chart.interaction('active-region');
        chart
            .interval()
            .adjust('stack')
            .position('city*value')
            .color('type*city', (type, city) => {
                if (type === '首都人口') {
                    return '#1890ff';
                }
                if (type === '城市人口') {
                    return '#ced4d9';
                }
                if (type === '农村人口') {
                    return '#f0f2f3';
                }
                if (type === '首都人口' && city === '中国(北京)') {
                    return '#f5222d';
                }
            })
            .size(26)
            .label('value*type', (val, t) => {
                const color = t === '首都人口' ? 'white' : '#47494b';
                if (val < 0.05) {
                    return null;
                }
                return {
                    position: 'middle',
                    offset: 0,
                    style: {
                        fontSize: 12,
                        fill: color,
                        lineWidth: 0,
                        stroke: null,
                        shadowBlur: 2,
                        shadowColor: 'rgba(0, 0, 0, .45)',
                    },
                };
            });
        chart.render();
    })
    return (
        <div>
            <div ref={ref} className='bar'/>
        </div>
    );
}
export default MyChart;

2.3 App.tsx中引入组件

这样我们可以去App.tsx中引入该条形图组件

import './App.css';
import React from 'react';
import Bar1 from './components/Bar';


function App(){
  return (
      <div className='App'>
        <Bar1 />
      </div>
  )
}


export default App;

2.4 运行项目

3. CSS样式代码

如果我们需要做可视化大屏,那还需要编写css样式(className我们已经在Bar.tsx中定义为'bar')

3.1 在style目录下创建bar.css

css代码:

.bar{
    text-align: left;
    width: 50%;
    height: auto;
    padding: 2rem;
}

当然,我们也可以在现有的App.css写样式,为了容易区分,我就单独写。

3.2 在Bar.tsx中引入css样式

import '../style/bar.css'

3.3 运行项目

三、条形图组合大屏

如果需要源代码,请查看下方(或评论处)方法免费获取(下载代码的好处是不需要自己从头再搭环境了,只要npm install安装库后,即可运行)