【前端可视化】如何在React中优雅的使用ECharts
这片文章由最近公司的一个可视化项目有感而发,随着前端的飞速发展,近年来数据可视化越来越火,有些公司的业务跟地图、位置、大数据等脱离不开关系,所以数据可视化甚至成了单独的一门前端行业,比如在杭州地区的前端可视化职位不但有一定的需求量且高薪,
至今为止,已经有很多的可视化框架供我们选择,比如D3、ECharts、Leaflet....等等。
本文使用的可视化框架为ECharts。
看完本文你可以学到什么?
- 如何搭建react+ts+echarts项目
- typescript基础使用
- eCharts如何在react中更安全高效的使用
- eCharts的使用
- eCharts图表尺寸自适应
- 启蒙可视化项目思想
本文的源码地址:github.com/Gexle-Tuy/e…
项目准备
技术栈为:React+TypeScript+ECharts。既然提到优雅那肯定跟TS逃离不开关系,毕竟强大的类型系统能给我的🐶💩代码保驾护航,什么?我不会TS,我不看了,别急,本文不做过于复杂的类型检查,只在组件状态(state)、属性(props)上做基本使用,不会TS也能看的懂,废话不多说,咱们开始吧。
使用的为react官方脚手架create-react-app,但是默认启动的为正常的js项目,如果想加上typescript类型检查,我们可以去它的仓库地址查看使用语法。在github上找到facebook/create-react-app。找到目录packages/cra-template-typescript。 在README中就可以看见启动命令create-react-app my-app --template typescript。
项目搭建完成之后看看src下的index文件的后缀名是否为tsx而不是jsx,为tsx就说明ts项目搭建成功了,就可以开始咱们的高雅之旅了~
初探
前面瞎吧啦半天完全跟我们本文的主角ECharts没有关系呀,伞兵作者?别急,这就开始,首先安装ECharts。
npm i echarts
安装好之后该干什么?当然是来个官方的入门例子感受一下了啦,打开官网->快速入手->绘制一个简单的图表。
可以看到,每一个图表都需要一个DOM当作容器,在React中我们可以用ref来获取到DOM实例。
发现平时正常写的ref竟然报错了,这就是强大的ts发挥了作用,我们把鼠标放上去可以发现提示框有一大堆东西。
不能将类型“RefObject<unknown>”分配给类型“LegacyRef<HTMLDivElement> | undefined”。
不能将类型“RefObject<unknown>”分配给类型“RefObject<HTMLDivElement>”。
不能将类型“unknown”分配给类型“HTMLDivElement”。
.....
可以根据它的提示来解决这个问题,将ref加上类型检查,本文不对ts做过多介绍,只使用简单的基础类型检查,我们直接给它加上一个:any。
eChartsRef:any= React.createRef();
这样报错就消失了,可以理解为any类型就是没有类型检查,跟普通的js一样没有区别。真正的重点不在这里,所以就直接使用any,其实应该按照它的提示加上真正的类型检查RefObject<HTMLDivElement>
。
拿到实例之后,直接copy官方的配置项例子过来看看效果。
import React, { PureComponent } from "react";
import * as eCharts from "echarts";
export default class App extends PureComponent {
eChartsRef: any = React.createRef();
componentDidMount() {
const myChart = eCharts.init(this.eChartsRef.current);
let option = {
title: {
text: "ECharts 入门示例",
},
tooltip: {},
legend: {
data: ["销量"],
},
xAxis: {
data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"],
},
yAxis: {},
series: [
{
name: "销量",
type: "bar",
data: [5, 20, 36, 10, 10, 20],
},
],
};
myChart.setOption(option);
}
render() {
return <div ref={this.eChartsRef} style={{
width: 600,
height: 400,
margin: 100
}}></div>;
}
}
当图标的动态效果呈现在你眼前的时候是不是心动了,原来可视化这么简单,到这里你就会了最基本的使用了。
接下来就开始本文的重点!如何在react里封装图表组件动态渲染并自适应移动端。
正文
首先确定项目中我们要用到的图表,这里我选了四个最基本且常用的图表(折线图、趋势图、饼状图、柱状图)。
所有的图表都由无状态组件写(函数组件、Hooks),因为它们只负责拿到数据并渲染。并无自己维护的状态。
接下来就是封装图表组件,这里就不把四个表的代码都贴出来了,只拿一个折线图举例子。可以把拉下源码看下其他的图。
折线图:src/components/LineChart
import React, { useEffect, useRef } from 'react';
import { IProps } from "./type";
import * as echarts from "echarts";
const Index: React.FC<IProps> = (props) => {
const chartRef:any = useRef(); //拿到DOM容器
// 每当props改变的时候就会实时重新渲染
useEffect(()=>{
const chart = echarts.init(chartRef.current); //echart初始化容器
let option = { //配置项(数据都来自于props)
title: {
text: props.title ? props.title : "暂无数据",
},
xAxis: {
type: 'category',
data: props.xData,
},
yAxis: {
type: 'value'
},
series: [{
data: props.seriesData,
type: 'line'
}]
};
chart.setOption(option);
}, [props]);
return <div ref={chartRef} className="chart"></div>
}
export default Index;
同文件下新建一个type.ts,将要约束的props类型检查单独抽离出去,当然也可以直接写在index.tsx文件里面,看个人喜好。
type.ts
// 给props添加类型检查
export interface IProps {
title: string, //图表的标题(为string类型)
xData: string[], //图表x轴数据的数组(数字里面每一项都为string类型)
seriesData: number[], //跟x轴每个坐标点对应的数据(数字里面每一项都为number类型)
}
根据每张图表对应的配置项,选出你想要动态配置的属性,就可以写成props作为属性传递过来。(比如,一个项目里需要用到很多张折线图,但是每个图表的线条颜色是不一样的,就可以把color写成一个props作为属性值传递进来。)
封装好之后,我们在App.tsx中引入使用一下。
App.tsx
import React, { PureComponent } from "react";
import LineChart from "./components/LineChart/Index";
import "./App.css";
export default class App extends PureComponent {
eChartsRef: any = React.createRef();
state = {
lineChartData: {
//折线图模拟数据
xData: [
"2021/08/13",
"2021/08/14",
"2021/08/15",
"2021/08/16",
"2021/08/17",
"2021/08/18",
],
seriesData: [22, 19, 88, 66, 5, 90],
},
};
componentDidMount() {}
render() {
return (
<div className="homeWrapper">
{/* 折线图 */}
<div className="chartWrapper">
<LineChart
title="折线图模拟数据"
xData={this.state.lineChartData.xData}
seriesData={this.state.lineChartData.seriesData}
/>
</div>
</div>
);
}
}
如果使用LineChart组件的时候少传了任何一个属性,或者说属性传递的类型不对,那么就会直接报错,将报错扼杀在开发阶段,而不是运行代码阶段,而且还有一个好处就是,加上类型检查后会有强大的智能提示,普通的js项目写一个组件根本就不会提示你需要传递某些属性。
忘记传递某个属性
传递的类型不符合类型检查
效果如下:
这样一个基本的图表组件就完成了,但是都是我们模拟的数据,在真实的开发中数据都是来自于后端返回给我们,而且格式还不是我们想要的,那时候就需要我们自己处理下数据包装成需要的数据格式再传递。
这样封装成函数组件还有一个好处就是每当props改变的时候就会进行重新渲染。比如我在componentDidMount中开启一个定时器定时添加数据来模拟实时数据。
componentDidMount() {
setInterval(() => {
this.setState({
lineChartData: {
xData: [...this.state.lineChartData.xData, "2000/01/01"],
seriesData: [...this.state.lineChartData.seriesData, Math.floor(Math.random() * 100)],
}
})
}, 1500 );
}
这样就可以实现展示实时数据了,比如每秒的pv、uv数等等。我们把四个图表组件全部封装好之后的效果是这样的。
前三个图表的数据都来自实时数据模拟,最后一张饼状图直接在组件中写死数据了,有兴趣的小伙伴可以拉下源码自行把它实现成实时的,可以看option中的配置哪些需要配置的,单独抽离出来写在type.ts文件中。
移动端适配
啥?echarts没做移动端适配?当然不是,echarts的官网中就介绍了移动端的相关优化:echarts.apache.org/zh/feature.… 当然也有跨平台使用。
好像是那么回事,但感觉好像少了些什么,好像没有根据屏幕尺寸大小变化而自动发生调整尺寸。每次都要刷新一下也就是重新进入页面。
别着急,在它的API文档中,有这么一个方法,echarts创建的实例也就是通过echarts.init()之后的对象会有一个resize的方法。
我们可以监听窗口的变化,只要窗口尺寸变化了就调用resize方法。监听窗口的变化的方法很简单window.onresize
可以在创建组件对象的时候都添加上一个window.onresize方法。
注意:如果网页只有一个图表那么这么写是可以的,如果项目中图表不只一个的话,每个图表组件难道在后面都写一个window.onresize方法吗?这样写的话只有最后创建的组件会自适应屏幕尺寸大小,因为每创建一个组件都重新将window.onresize赋予为新的函数体了。
解决:我们可以写一个公用方法,每一次创建组件的时候都加入到一个数组中,当屏幕尺寸变化的时候,都去循环遍历这个数组中的每一项,然后调用resize方法。
src/util.js
const echartsDom = []; //所有echarts图表的数组
/**
* 当屏幕尺寸变化时,循环数组里的每一项调用resize方法来实现自适应。
* @param {*} eDom
*/
export function echartsResize(eDom) {
echartsDom.push(eDom);
window.onresize = () => {
echartsDom.forEach((it)=>{
it.resize();
})
};
}
写好方法之后,在每个图表组件设置好option之后将他添加到此数组内,然后当屏幕尺寸变化后就可以将每个图表变成自适应的了。
这样之后每个图表就都可以自适应屏幕尺寸啦~
结语
本文主要介绍了如何在react中更安全高效的使用eCharts,所涉及的ts都为最基础的类型检查(有兴趣的同学可以自行拓展),只是为了给各位提供一个我在写一个eCharts项目的时候如何去做和管理项目,文章有错误的地方欢迎指出,大佬勿喷,大家伙儿有更好的思路和想法欢迎大家积极留言。感谢观看~
链接:https://juejin.cn/post/7000551946029858830