Skip to content
On this page

使用echarts

使用echarts主要有两个痛点:

  • echarts如果使用全量加载,打包后的体积会比较大,为了减小打包后的体积,我们使用按需引入的方式。
  • 我们在做echarts图表的时候,其实只希望传入option属性即可,浏览器resize需要对样式做实时的调整,以及销毁监听事件等,我们希望放在一个公共的hooks中。

依赖注入echarts模块

ts
import * as echarts from 'echarts/core'

// 引入柱状图和饼状图图表,图表后缀都为 Chart,具体为 图标名称+Chart (注意图表名称为首字母大写)
import { BarChart, CustomChart, LineChart, PictorialBarChart } from 'echarts/charts'

// 引入提示框,标题,直角坐标系,数据集,内置数据转换器组件,组件后缀都为 Component
import { GridComponent, LegendComponent, TitleComponent, TooltipComponent } from 'echarts/components'

// 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步
import { SVGRenderer } from 'echarts/renderers'

echarts.use([
	BarChart,
	LineChart,
	PictorialBarChart,
	CustomChart,
	TitleComponent,
	TooltipComponent,
	GridComponent,
	LegendComponent,
	SVGRenderer
])

export default echarts

useEcharts 函数

ts
import { useTimeoutFn } from '@dt-frames/core'
import { useMenu } from '@dt-frames/ui'
import { tryOnUnmounted } from '@vueuse/core'
import type { EChartsOption } from 'echarts'
import { Ref } from 'vue'
import echarts from './echarts'

interface Fn<T = any, R = T> {
	(...arg: T[]): R
}

/**
 * @param elRef 绑定的dom实例
 * @param theme 图表将使用的主题
 */
export function useEcharts(elRef: Ref<HTMLDivElement>, theme: 'light' | 'dark' = 'dark') {
	const resizeFn: Fn = resize

	// 获取已经缓存的配置信息 用于重新渲染
	const cacheOptions = ref({}) as Ref<EChartsOption>
	const getOptions = computed(() => {
		if (theme !== 'dark') {
			return cacheOptions.value as EChartsOption
		}
		return {
			backgroundColor: 'transparent',
			...cacheOptions.value
		} as EChartsOption
	})

	// 初始化echarts
	let chartInstance: echarts.ECharts | null = null
	let removeResizeFn: Fn = () => {}
	function initCharts() {
		const el = unref(elRef)
		if (!el || !unref(el)) {
			error('未找到元素!')
			return
		}

		chartInstance = echarts.init(el, theme)

		// 停止resize事件
		removeResizeFn()

		const [start, stop] = windowResize(resizeFn, 100)
		start()
		removeResizeFn = stop
	}

	// 设置配置信息
	function setOptions(options: EChartsOption, clear = true) {
		cacheOptions.value = options

		return new Promise(resolve => {
			nextTick(() => {
				useTimeoutFn(() => {
					if (!chartInstance) {
						initCharts()

						if (!chartInstance) return
					}

					clear && chartInstance?.clear()

					chartInstance?.setOption(unref(getOptions))
					resolve(null)
				}, 30)
			})
		})
	}

	function resize() {
		chartInstance?.resize({
			animation: {
				duration: 300,
				easing: 'quadraticIn'
			}
		})
	}

	// 左侧菜单折叠后重新渲染
	const { getCollapsed } = useMenu()
	watch(getCollapsed, () => {
		useTimeoutFn(() => {
			resizeFn()
		}, 300)
	})

	// 获取echart实例
	function getInstance() {
		if (!chartInstance) {
			initCharts()
		}
		return chartInstance
	}

	// 销毁实例及window resize监听
	tryOnUnmounted(() => {
		if (!chartInstance) return
		removeResizeFn()

		chartInstance.dispose()
		chartInstance = null
	})

	return {
		setOptions,
		resize,
		echarts,
		getInstance
	}
}

页面使用

ts
<!-- html -->
<div ref="elRef"></div>

// script
const elRef = ref(null)
const { setOptions } = useEcharts(elRef)

onMounted(() => {
    setOptions({
        xAxis: {
            // ...
        },
        grid: {
            // ...
        }
    })
})