这里我们要在自己搭建的react项目中使用ECharts,我们可以在ECharts官网上看到有一种方式是在 webpack 中使用 ECharts,我们需要的就是这种方法。
我们在使用ECharts之前要先安装ECharts,在以往的开发模式中,我们很多使用就是把官网中的ECharts的核心js文件导入到我们的html或者是jsp等文件里面,但是在react项目中,我们可以直接使用node.js的npm命令安装:
npm install echarts --save
这个时候我们的ECharts就被下载到项目中的node_modules文件夹中,这个时候我们就可以在编写的react组件中使用ECharts了,下面直接看代码:
import React, { Component } from 'react'; // 引入 ECharts 主模块 import echarts from 'echarts/lib/echarts'; // 引入柱状图 import 'echarts/lib/chart/bar'; // 引入提示框和标题组件 import 'echarts/lib/component/tooltip'; import 'echarts/lib/component/title'; class EchartsTest extends Component { componentDidMount() { // 基于准备好的dom,初始化echarts实例 var myChart = echarts.init(document.getElementById('main')); // 绘制图表 myChart.setOption({ title: { text: 'ECharts 入门示例' }, tooltip: {}, xAxis: { data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"] }, yAxis: {}, series: [{ name: '销量', type: 'bar', data: [5, 20, 36, 10, 10, 20] }] }); } render() { return ( <div id="main" style={{ width: 400, height: 400 }}></div> ); } } export default EchartsTest;
这里我们在使用ECharts的时候要在render()方法中return一个有id和大小的div,然后在componentDidMount()方法中像我们以往的方式一样渲染数据,这里都是一些静态的数据,当我们的数据是动态获取的时候,我们就要把渲染ECharts的数据放入到state中,这样我们才能够做出有交互性的动态效果。
下面是展示代码的效果:
在 React 项目中使用 ECharts
说明
重要文件版本
- “react”: “^16.2.0”
- “echarts”: “^4.0.2”
- “redux”: “^3.7.2”,
- “react-router-dom”: “^4.2.2”
- “react-router-redux”: “^5.0.0-alpha.9”
- “react-hot-loader”: “^4.0.0-beta.21”
在 React 项目中使用 ECharts 生成图表,与使用其他第三方库没有太大的区别,只要在 React 的组件
componentDidMount
声明周期中初始化 ECharts 图表,然后在每次更新组件时调用 ECharts 实例的setOption()
方法更新配置即可,以下记录了使用当中的一些细节
编写 <Chart/>
图表组件
>
/*
* ECharts 组件基础部分
* */
import React, { PureComponent } from 'react';
import * as echarts from 'echarts';
import 'zrender/lib/svg/svg';
import throttle from '../../utils/throttle'; // 一个节流函数
export default class Chart extends PureComponent {
constructor(props) {
super(props);
this.state = {
width: '100%',
height: '100%'
};
this.chart = null;
}
/*
注意:
虽然在 componentDidMount 中组件已经被装配,
但是如果设置容器宽高为百分比的值,那么容器的 clientWidth 和 clientHeight 有可能还处于计算中
这个时候如果在容器中实例化 echarts,echarts 获得的 clientWidth 和 clientHeight 不一定是我们预期的,
因此这里使用了定时器延迟实例化,也可以提前计算出像素之后 赋值给 width、height,这样不是百分比就没有问题
*/
async componentDidMount() {
console.log('did mount');
// 初始化图表
await this.initChart(this.el);
// 将传入的配置(包含数据)注入
this.setOption(this.props.option);
// 监听屏幕缩放,重新绘制 echart 图表
window.addEventListener('resize', throttle(this.resize, 100));
}
componentDidUpdate() {
// 每次更新组件都重置
this.setOption(this.props.option);
}
componentWillUnmount() {
// 组件卸载前卸载图表
this.dispose();
}
render() {
const { width, height } = this.state;
return (
<div
className="default-chart"
ref={el => (this.el = el)}
style={{ width, height }}
/>
);
}
initChart = el => {
// renderer 用于配置渲染方式 可以是 svg 或者 canvas
const renderer = this.props.renderer || 'canvas';
console.log(renderer);
return new Promise(resolve => {
setTimeout(() => {
this.chart = echarts.init(el, null, {
renderer,
width: 'auto',
height: 'auto'
});
resolve();
}, 0);
});
};
setOption = option => {
if (!this.chart) {
return;
}
const notMerge = this.props.notMerge;
const lazyUpdate = this.props.lazyUpdate;
this.chart.setOption(option, notMerge, lazyUpdate);
};
dispose = () => {
if (!this.chart) {
return;
}
this.chart.dispose();
this.chart = null;
};
resize = () => {
this.chart && this.chart.resize();
};
getInstance = () => {
return this.chart;
};
}
其他组件中引用图表组件
/*
Heatmap 热力图
*/
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import 'echarts/lib/component/tooltip';
import 'echarts/lib/component/title';
import 'echarts/lib/component/grid';
import 'echarts/lib/chart/heatmap';
import 'echarts/lib/component/visualMap';
import Chart from '../components/Chart/index';
import { getChart } from '../modules/charts/actions';
class HeatmapChart extends Component {
constructor(props) {
super(props);
this.state = {
renderer: 'canvas'
};
}
async componentWillMount() {
// 装载前 获取数据
const path = '/heatmap';
const key = 'heatmap';
await this.props.getChart({ path, key });
}
render() {
const renderer = this.state.renderer;
const option = this.getOption();
return <Chart renderer={renderer} option={option} />;
}
// getOption 这个函数主要用于配置 option,包括将数据配置进去
// 也可以将其放在 state 中,然后通过 setState 更新
getOption = () => {
// 组装数据,返回配置 option
const currentData = this.props.charts.heatmap;
return {
tooltip: {
position: 'top'
},
grid: {
containLabel: true
},
xAxis: {
type: 'category',
splitArea: {
show: true
},
boundaryGap: true
},
yAxis: {
type: 'category',
splitArea: {
show: true
},
boundaryGap: true
},
visualMap: {
min: 0,
max: 20,
calculable: true,
left: 'left',
bottom: 'bottom',
dimension: 2
},
dataset: {
source: currentData
},
series: [
{
name: '热力图',
type: 'heatmap',
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
},
encode: {
x: 0,
y: 1
},
tooltip: {
formatter(params) {
const [x, y, value] = params.data;
return [
`x: ${x}`,
`y: ${y}`,
`value: ${value}`
].join('<br/>');
}
}
}
]
};
};
}
export default connect(
state => ({ charts: state.charts }),
dispatch => ({
getChart: bindActionCreators(getChart, dispatch)
})
)(HeatmapChart);
NO!!!
在这里,我使用echarts提供的模块化加载方式,实现了几个react-echarts图表组件:react-echarts图表在线渲染查看
你可以打开控制台,观察每个图表组件的加载情况。
但是,如果你认为仅仅是模块导入echarts的tooltip、grid这些插件就完事了,那么我也用不着写这篇文章分享了,下面会讲到另外一种异步加载方式。
导入echarts,最烦人的是什么?配置option是其一,其二就是极其庞大的echarts!特别是管理后台使用到echarts的时候,如果需要同个页面展示数十种图表类型,你就该好好考虑性能问题了。
插件版本号
"echarts": "^3.6.2",
"react": "^15.6.1",
"react-dom": "^15.6.1"
实现了哪些图表组件
1、饼图
2、柱状图
3、折线图
4、散点图
5、地图
6、雷达图
7、k线图
我们不总是需要插件
实现这些echarts-react组件的目的,是告诉大家,react可以不需要引入第三方插件,使用echarts,不要惧怕react组件!
看过很多人说react难写,因为他们习惯了在jQuery开发模式下导入echarts、swiper、d3等插件。而突然切换到react中,就产生了不知所措的感觉。
如何在react中导入第三方插件,成为了他们心中的痛点,所以一些人就认为需要别人封装好的echarts-react插件或者其他react插件,才能使用,这种想法是错的。
echarts体积太大,使用模块化加载
以柱状图为例子,我们根据需要渲染的插件采取模块导入,不渲染的组件不导入,最大程度减小js。
import echarts from 'echarts/lib/echarts' //必须
import 'echarts/lib/component/tooltip'
import 'echarts/lib/component/grid'
import 'echarts/lib/chart/bar'
组件化开发的福音,react组件模块化加载
demo中采用单个echarts组件异步打包加载的模式,因为echarts组件普遍偏大,即使压缩也效果不明显,所以异步加载是最好的方式。
import { pieOption, barOption, lineOption, scatterOption, mapOption, radarOption, candlestickOption } from './optionConfig/options'
const PieReact = asyncComponent(() => import(/* webpackChunkName: "PieReact" */'./EchartsDemo/PieReact')) //饼图组件
const BarReact = asyncComponent(() => import(/* webpackChunkName: "BarReact" */'./EchartsDemo/BarReact')) //柱状图组件
const LineReact = asyncComponent(() => import(/* webpackChunkName: "LineReact" */'./EchartsDemo/LineReact')) //折线图组件
const ScatterReact = asyncComponent(() => import(/* webpackChunkName: "ScatterReact" */'./EchartsDemo/ScatterReact')) //散点图组件
const MapReact = asyncComponent(() => import(/* webpackChunkName: "MapReact" */'./EchartsDemo/MapReact')) //地图组件
const RadarReact = asyncComponent(() => import(/* webpackChunkName: "RadarReact" */'./EchartsDemo/RadarReact')) //雷达图组件
const CandlestickReact = asyncComponent(() => import(/* webpackChunkName: "CandlestickReact" */'./EchartsDemo/CandlestickReact')) //k线图组件
启动项目
//安装
npm install
//启动
npm start
打包项目
npm run build
实现方案介绍
1、每个图表单独封装成一个组件,通过参数传递数据,你会发现,图表内部代码几乎完全一样,只有import的类型不同。
2、异步加载是提高图表加载性能的最佳方式,不管是服务端还是客户端渲染。
3、在这些demo中,我认为对你来说最有价值的是react组件异步加载模式,很多人异步加载组件是通过拆分路由的方式,而非路由组件的异步加载,并不多人去尝试。但我想告诉你的是,
非路由组件的异步加载会将你的庞大的父组件拆分的更细,体积更小,加载的更加流畅。
方法一:
echarts-for-react 是一个非常简单的针对于 React 的 Echarts 封装插件。
和使用所有其他插件一样,首先,我们需要 install 它:
第一步:
npminstall --save echarts(依赖)
npminstall --save echarts-for-react
第二步:
在我们的项目中导入:
importReactEcharts from 'echarts-for-react'
第三步:
在 render 函数中使用:
option={this.getOption}
notMerge={true}
lazyUpdate={true}
style={{width: ‘400px’, height: ‘400px’}}
/>
组件基本参数介绍:
option:接收一个对象,该对象为 echarts 的配置项,详见: http://echarts.baidu.com/option.html#title
notMerge:可选,是否不跟之前设置的 option 进行合并,默认为 false,即合并。
LazyUpdate:可选,在设置完 option 后是否不立即更新图表,默认为 false,即立即更新。
style:echarts 容器 div 大小,默认:{height: ‘300px’}
方法二:
当然,我们也不是真的需要一个 react-echarts 插件,我们可以使用 echarts 提供的模块化加载方法,按需导入我们需要的图表:
首先,我们需要在项目中导入 echarts:
importecharts from 'echarts/lib/echarts' //必须
import'echarts/lib/component/tooltip' //图表提示框(按需)
import'echarts/lib/component/grid' //图表网格(按需)
import 'echarts/lib/chart/bar' //引入柱状图(按需)
import'echarts/lib/chart/line’ //引入折线图(按需)
然后:我们需要在 render 函数中为图表放好一个容器:
{this.chartContainer = refs}} style={{width: ‘400px’, height: ‘400px’}}>
最后,我们需要在合适的生命周期中绘制我们的图表:
letmyChart = echarts.init(this.chartContainer)
letoption = {//echarts配置项}
myChart.setOption(option,true)
好了,echarts 已经成功的在项目中跑起来啦!
-END-