摘 要
之前Ofter详细介绍过用Vue从零开始编写可视化大屏,今天我们来介绍下如何用React编写可视化图表。为什么我们还要学习React?因为轻量化,像antv可视化图表就全部用的React语言,虽然ant也出了个viser(支持React/Vue/Angular 3种前端语言),但是图表没有antv完整,文档也不太清楚。
知识点1:React和Vue的区别
React | Vue | |
组件 | react主打轻量级,细化了组件功能,就是你需要什么,就去一个个引入(比如要使用链接功能,就需要引入<Link>组件)。因此,React需要开发者有一定的前端基础,更适合专业人士搭建轻型系统。 | vue封装组件功能更强大,即便是初学者,不需要知道很多原理,就可以引入组件使用。因此,当功能越来越多时,Vue引入的组件冗余内容会越来越多。 |
数据 | react整体是函数式的思想,把组件设计成纯组件,状态和逻辑通过参数传入,属于单向数据流,数据可视化往往不需要双向数据流。 | vue的思想是响应式的,也就是基于是数据可变的,通过对每一个属性建立Watcher来监听,当属性变化的时候,响应式的更新对应的虚拟dom。 |
js | react的思路是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,一般通过<div id='a' ref='b'>中的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 运行项目
三、条形图组合大屏
Ofter采用的是flex布局,比较简单,如果想要学习的,可以进入以下链接:学会用HTML-flex布局,制作漂亮的网页想要做一个好看的小系统,我们还要学一些前端的知识,今天OF将讲解如何用pycharm(全栈开发不错的工具)做一张好看的网页,以后我们就可以自己开发网页/网站放到互联网上。https://mp.weixin.qq.com/s/Q_IFlyUOKXiSXox6Aadnsg
四、总结及下期预告
我们总结一下,本篇Ofter讲解了如何用vite, react, ts, antv/g2做一个轻量级、静态的可视化图表,以及vue、react之间的一些差异化表现。如果我们需要做一个图表更完整、性能更好、数据更动态的大屏,因为篇幅有限,Ofter将在下期为大家介绍:
- 增加Router路由;
- 增加懒加载;
- 动态获取数据。
如果需要源代码,请查看下方(或评论处)方法免费获取(下载代码的好处是不需要自己从头再搭环境了,只要npm install安装库后,即可运行)