阅读 3574
【前端可视化】如何在React中优雅的使用ECharts🍉

【前端可视化】如何在React中优雅的使用ECharts🍉

前言

这片文章由最近公司的一个可视化项目有感而发,随着前端的飞速发展,近年来数据可视化越来越火,有些公司的业务跟地图、位置、大数据等脱离不开关系,所以数据可视化甚至成了单独的一门前端行业,比如在杭州地区的前端可视化职位不但有一定的需求量且高薪,

截屏2021-08-25 下午10.57.37.png

至今为止,已经有很多的可视化框架供我们选择,比如D3EChartsLeaflet....等等。

本文使用的可视化框架为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。 image

项目搭建完成之后看看src下的index文件的后缀名是否为tsx而不是jsx,为tsx就说明ts项目搭建成功了,就可以开始咱们的高雅之旅了~

初探

前面瞎吧啦半天完全跟我们本文的主角ECharts没有关系呀,伞兵作者?别急,这就开始,首先安装ECharts。

npm i echarts
复制代码

安装好之后该干什么?当然是来个官方的入门例子感受一下了啦,打开官网->快速入手->绘制一个简单的图表。

可以看到,每一个图表都需要一个DOM当作容器,在React中我们可以用ref来获取到DOM实例。

image

发现平时正常写的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>;
  }
}
复制代码

gif

当图标的动态效果呈现在你眼前的时候是不是心动了,原来可视化这么简单,到这里你就会了最基本的使用了。

接下来就开始本文的重点!如何在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项目写一个组件根本就不会提示你需要传递某些属性。

忘记传递某个属性 image

传递的类型不符合类型检查 image

效果如下:

gif

这样一个基本的图表组件就完成了,但是都是我们模拟的数据,在真实的开发中数据都是来自于后端返回给我们,而且格式还不是我们想要的,那时候就需要我们自己处理下数据包装成需要的数据格式再传递。

这样封装成函数组件还有一个好处就是每当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 );
}
复制代码

gif

这样就可以实现展示实时数据了,比如每秒的pv、uv数等等。我们把四个图表组件全部封装好之后的效果是这样的。

gif

前三个图表的数据都来自实时数据模拟,最后一张饼状图直接在组件中写死数据了,有兴趣的小伙伴可以拉下源码自行把它实现成实时的,可以看option中的配置哪些需要配置的,单独抽离出来写在type.ts文件中。

移动端适配

啥?echarts没做移动端适配?当然不是,echarts的官网中就介绍了移动端的相关优化:echarts.apache.org/zh/feature.… 当然也有跨平台使用。

gif

好像是那么回事,但感觉好像少了些什么,好像没有根据屏幕尺寸大小变化而自动发生调整尺寸。每次都要刷新一下也就是重新进入页面。

别着急,在它的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之后将他添加到此数组内,然后当屏幕尺寸变化后就可以将每个图表变成自适应的了。

这样之后每个图表就都可以自适应屏幕尺寸啦~

gif

结语

本文主要介绍了如何在react中更安全高效的使用eCharts,所涉及的ts都为最基础的类型检查(有兴趣的同学可以自行拓展),只是为了给各位提供一个我在写一个eCharts项目的时候如何去做和管理项目,文章有错误的地方欢迎指出,大佬勿喷,大家伙儿有更好的思路和想法欢迎大家积极留言。感谢观看~

文章分类
前端
文章标签