{T}

ECharts 快速入门

ECharts 简介

ECharts 是使用 JavaScript 实现的开源可视化库,可以流畅地运行在 PC 和移动设备上,兼容当前绝大部分浏览器。底层依赖矢量图形库 ZRender,提供直观、交互丰富、可高度个性化定制的数据可视化图表。

ECharts 特性

丰富的可视化类型

ECharts 提供常规的折线图、柱状图、散点图、饼图、K线图,用于统计的盒形图,用于地理数据可视化的地图、热力图、线图,用于关系数据可视化的关系图、treemap、旭日图,多维数据可视化的平行坐标,还有用于 BI 的漏斗图、仪表盘,并且支持图与图之间的混搭。

除了内置的图表,ECharts 还提供了自定义系列,只需要传入一个 renderItem 函数,就可以从数据映射到任何你想要的图形。

多种数据格式支持

ECharts 内置的 dataset 属性(4.0+)支持直接传入包括二维表、key-value 等多种格式的数据源,通过简单的设置 encode 属性就可以完成从数据到图形的映射。这种方式更符合可视化的直觉,省去了大部分场景下数据转换的步骤。

千万数据的前端展现

通过增量渲染技术(4.0+),配合各种细致的优化,ECharts 能够展现千万级的数据量,并且在这个数据量级依然能够进行流畅的缩放平移等交互。

移动端优化

ECharts 针对移动端交互做了细致的优化,例如移动端小屏上适于用手指在坐标系中进行缩放、平移。细粒度的模块化和打包机制可以让 ECharts 在移动端也拥有很小的体积。

多渲染方案

ECharts 支持以 Canvas、SVG(4.0+)、VML 的形式渲染图表。VML 可以兼容低版本 IE,SVG 使得移动端不再为内存担忧,Canvas 可以轻松应对大数据量和特效的展现。

深度的交互式数据探索

ECharts 提供图例、视觉映射、数据区域缩放、tooltip、数据刷选等开箱即用的交互组件,可以对数据进行多维度数据筛取、视图缩放、展示细节等交互操作。

安装方式

Apache ECharts 提供了多种安装方式,你可以根据项目的实际情况选择以下任意一种方式安装:

npm 安装(推荐)

bash
npm install echarts

全局引入

javascript
import * as echarts from '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]
    }
  ]
})

按需引入

javascript
import * as echarts from 'echarts/core'
import { BarChart } from 'echarts/charts'
import {
  TitleComponent,
  TooltipComponent,
  GridComponent,
  DatasetComponent,
  TransformComponent
} from 'echarts/components'
import { LabelLayout, UniversalTransition } from 'echarts/features'
import { CanvasRenderer } from 'echarts/renderers'

echarts.use([
  TitleComponent,
  TooltipComponent,
  GridComponent,
  DatasetComponent,
  TransformComponent,
  BarChart,
  LabelLayout,
  UniversalTransition,
  CanvasRenderer
])

var myChart = echarts.init(document.getElementById('main'))
myChart.setOption({
  // ...
})

注意:按需引入时不再提供任何渲染器,需要选择引入 CanvasRenderer 或者 SVGRenderer

TypeScript 中按需引入

typescript
import * as echarts from 'echarts/core'
import { BarChart, LineChart } from 'echarts/charts'
import {
  TitleComponent,
  TooltipComponent,
  GridComponent,
  DatasetComponent,
  TransformComponent
} from 'echarts/components'
import { LabelLayout, UniversalTransition } from 'echarts/features'
import { CanvasRenderer } from 'echarts/renderers'
import type {
  BarSeriesOption,
  LineSeriesOption
} from 'echarts/charts'
import type {
  TitleComponentOption,
  TooltipComponentOption,
  GridComponentOption,
  DatasetComponentOption
} from 'echarts/components'
import type { ComposeOption } from 'echarts/core'

type ECOption = ComposeOption<
  | BarSeriesOption
  | LineSeriesOption
  | TitleComponentOption
  | TooltipComponentOption
  | GridComponentOption
  | DatasetComponentOption
>

echarts.use([
  TitleComponent,
  TooltipComponent,
  GridComponent,
  DatasetComponent,
  TransformComponent,
  BarChart,
  LineChart,
  LabelLayout,
  UniversalTransition,
  CanvasRenderer
])

const option: ECOption = {
  // ...
}

CDN 获取

可以从以下免费 CDN 中获取和引用 ECharts:

html
<script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>

从 GitHub 获取

apache/echarts 项目的 release 页面可以找到各个版本的链接。点击下载页面下方 Assets 中的 Source code,解压后 dist 目录下的 echarts.js 即为包含完整 ECharts 功能的文件。

在线定制

如果只想引入部分模块以减少包体积,可以使用 ECharts 在线定制 功能。

初始化

在 HTML 中定义容器

通常需要在 HTML 中先定义一个 <div> 节点,并且通过 CSS 使得该节点具有宽度和高度。

html
<div id="main" style="width: 600px; height: 400px;"></div>

<script type="text/javascript">
  var myChart = echarts.init(document.getElementById('main'))
</script>

注意:使用这种方法在调用 echarts.init 时需保证容器已经有宽度和高度。

指定图表大小

如果图表容器不存在宽度和高度,或者希望图表宽度和高度不等于容器大小,可以在初始化时指定大小:

html
<div id="main"></div>

<script type="text/javascript">
  var myChart = echarts.init(document.getElementById('main'), null, {
    width: 600,
    height: 400
  })
</script>

响应容器大小变化

监听 resize 事件

当容器大小改变时,可以监听页面的 resize 事件,然后调用 echartsInstance.resize 改变图表大小:

html
<style>
  #main,
  html,
  body {
    width: 100%;
  }
  #main {
    height: 400px;
  }
</style>

<div id="main"></div>

<script type="text/javascript">
  var myChart = echarts.init(document.getElementById('main'))
  window.addEventListener('resize', function () {
    myChart.resize()
  })
</script>

提示:可以通过浏览器的 ResizeObserver API 实现更细粒度的监听。

为图表设置特定大小

除了直接调用 resize() 不含参数的形式之外,还可以指定宽度和高度:

javascript
myChart.resize({
  width: 800,
  height: 400
})

容器销毁与重建

当图表容器被销毁后重新添加时,正确的做法是:

  1. 在容器被销毁时调用 echartsInstance.dispose 销毁实例
  2. 在容器重新添加后再次调用 echarts.init 初始化

提示:在容器节点被销毁时,应调用 echartsInstance.dispose 以销毁实例释放资源,避免内存泄漏。

入门示例

基础柱状图

html
<!DOCTYPE html>
<html>
  <head>
    <script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
    <style>
      #chart {
        width: 800px;
        height: 400px;
      }
    </style>
  </head>
  <body>
    <div id="chart"></div>
    <script>
      const chartDom = document.getElementById('chart')
      const chart = echarts.init(chartDom)
      chart.setOption({
        title: {
          text: '快速入门 ECharts 开发'
        },
        xAxis: {
          data: ['食品', '数码', '服饰', '箱包']
        },
        yAxis: {},
        series: {
          type: 'bar',
          data: [100, 120, 90, 150]
        }
      })
    </script>
  </body>
</html>

ECharts 绘图流程

  1. 引入 ECharts 库
  2. 编写渲染容器 DOM,添加 widthheight 样式属性
  3. 获取渲染 DOM 对象
  4. 初始化 ECharts 对象
  5. 编写 option 参数
  6. 调用 setOption 完成渲染

升级方式

官网下载页可以下载最新版的源码和编译产物。如果使用 npm 安装,升级方式为:

bash
npm install echarts@latest

框架集成

Vue 3 集成

基础使用

Vue SFC
<template>
  <div ref="chartRef" style="width: 600px; height: 400px"></div>
</template>

<script setup>
import { ref, onMounted, onUnmounted, watch } from 'vue'
import * as echarts from 'echarts'

const props = defineProps({
  option: { type: Object, required: true }
})

const chartRef = ref(null)
let chart = null

onMounted(() => {
  chart = echarts.init(chartRef.value)
  chart.setOption(props.option)
  
  // 响应式
  window.addEventListener('resize', handleResize)
})

onUnmounted(() => {
  window.removeEventListener('resize', handleResize)
  chart?.dispose()
})

watch(() => props.option, (newOption) => {
  chart?.setOption(newOption)
}, { deep: true })

function handleResize() {
  chart?.resize()
}
</script>

使用 Composable 封装

javascript
// useChart.js
import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue'
import * as echarts from 'echarts'

export function useChart(option) {
  const chartRef = ref(null)
  let chart = null

  const initChart = () => {
    if (chartRef.value) {
      chart = echarts.init(chartRef.value)
      chart.setOption(option.value)
    }
  }

  const resize = () => chart?.resize()
  const setOption = (opt) => chart?.setOption(opt)

  onMounted(() => {
    nextTick(initChart)
    window.addEventListener('resize', resize)
  })

  onUnmounted(() => {
    window.removeEventListener('resize', resize)
    chart?.dispose()
  })

  watch(option, (newOpt) => setOption(newOpt), { deep: true })

  return { chartRef, chart, resize, setOption }
}

React 集成

基础使用

tsx
import { useEffect, useRef } from 'react'
import * as echarts from 'echarts'

interface ChartProps {
  option: echarts.EChartsOption
  style?: React.CSSProperties
}

function Chart({ option, style }: ChartProps) {
  const chartRef = useRef<HTMLDivElement>(null)
  const chartInstance = useRef<echarts.ECharts | null>(null)

  useEffect(() => {
    if (chartRef.current) {
      chartInstance.current = echarts.init(chartRef.current)
      chartInstance.current.setOption(option)
    }

    const handleResize = () => chartInstance.current?.resize()
    window.addEventListener('resize', handleResize)

    return () => {
      window.removeEventListener('resize', handleResize)
      chartInstance.current?.dispose()
    }
  }, [])

  useEffect(() => {
    chartInstance.current?.setOption(option)
  }, [option])

  return <div ref={chartRef} style={{ width: 600, height: 400, ...style }} />
}

export default Chart

使用自定义 Hook

typescript
// useChart.ts
import { useEffect, useRef, useCallback } from 'react'
import * as echarts from 'echarts'

export function useChart(option: echarts.EChartsOption) {
  const chartRef = useRef<HTMLDivElement>(null)
  const chart = useRef<echarts.ECharts | null>(null)

  const resize = useCallback(() => chart.current?.resize(), [])
  const setOption = useCallback((opt: echarts.EChartsOption) => {
    chart.current?.setOption(opt)
  }, [])

  useEffect(() => {
    if (chartRef.current) {
      chart.current = echarts.init(chartRef.current)
      chart.current.setOption(option)
    }

    window.addEventListener('resize', resize)
    return () => {
      window.removeEventListener('resize', resize)
      chart.current?.dispose()
    }
  }, [])

  return { chartRef, resize, setOption }
}

// 使用
function MyChart() {
  const { chartRef } = useChart({
    xAxis: { data: ['A', 'B', 'C'] },
    yAxis: {},
    series: [{ type: 'bar', data: [10, 20, 30] }]
  })

  return <div ref={chartRef} style={{ width: 600, height: 400 }} />
}

最佳实践

1. 按需引入

推荐使用按需引入减少打包体积:

javascript
// echarts.config.js
import * as echarts from 'echarts/core'
import { BarChart, LineChart, PieChart } from 'echarts/charts'
import {
  TitleComponent,
  TooltipComponent,
  LegendComponent,
  GridComponent
} from 'echarts/components'
import { CanvasRenderer } from 'echarts/renderers'

echarts.use([
  TitleComponent,
  TooltipComponent,
  LegendComponent,
  GridComponent,
  BarChart,
  LineChart,
  PieChart,
  CanvasRenderer
])

export default echarts

2. 内存管理

避免内存泄漏,正确销毁实例:

javascript
// 组件销毁时调用
onUnmounted(() => {
  chart.dispose()  // 销毁实例
  chart = null     // 释放引用
})

// 移除事件监听
chart.off('click') // 移除特定事件

3. 响应式处理

javascript
// 使用 ResizeObserver(推荐)
const observer = new ResizeObserver(() => {
  chart.resize()
})
observer.observe(container)

// 销毁时
observer.disconnect()

4. 性能优化

javascript
// 初始化时开启脏矩形渲染
echarts.init(dom, null, {
  useDirtyRect: true,
  renderer: 'canvas'  // 大数据量时优先使用 canvas
})

// 大数据量时开启优化
series: [{
  type: 'scatter',
  large: true,        // 大数据优化
  largeThreshold: 2000, // 阈值
  progressive: 200    // 渐进式渲染
}]

5. 防抖处理

频繁更新数据时使用防抖:

javascript
import { debounce } from 'lodash'

const updateChart = debounce((data) => {
  chart.setOption({ series: [{ data }] })
}, 300)

相关链接