基于Vue3+echarts的封装
技术文档:ECharts Vue 组件封装指南
组件概述
封装了一个基于 Vue 3 和 ECharts 的可复用图表组件,支持动态配置、事件监听和响应式布局。组件通过 Echart.vue 实现核心功能,依赖 plugins/echarts 的模块化导入和 events.ts 的事件绑定配置。
核心文件说明
1. events.ts
定义所有支持的 ECharts 事件类型,用于自动绑定到组件 emits:
export const EVENTS = [
"click", "dblclick", "mouseover",
"legendselectchanged", "datazoom",
"timelinechanged", "brushselected",
// ...其他事件
];
2. plugins/echarts
按需导入 ECharts 模块以优化体积:
import * as echarts from 'echarts/core';
import { BarChart, LineChart } from 'echarts/charts';
import { TitleComponent, TooltipComponent } from 'echarts/components';
import { CanvasRenderer } from 'echarts/renderers';
echarts.use([
BarChart,
TitleComponent,
CanvasRenderer
// ...其他模块
]);
3. Echart.vue
核心组件逻辑:
- 响应式处理宽高(支持字符串/数字)
- 自动初始化/销毁图表实例
- 监听选项变化实时更新图表
- 全局窗口 resize 防抖处理
使用方法
基础用法
通过 options 传递 ECharts 配置:
事件监听
通过 emits 捕获图表交互事件:
高级功能
动态更新
深度监听 options 变化自动更新图表:
watch(
() => options.value,
(newOpts) => {
echartRef?.setOption(newOpts);
},
{ deep: true }
);
性能优化
- 使用 Lodash 的
debounce处理 resize 事件 - 通过
onActivated保持图表尺寸正确性 - 按需导入 ECharts 模块减少打包体积
注意事项
- 组件销毁时会自动移除事件监听和图表实例
- 过渡动画期间通过
contentResizeHandler处理容器尺寸变化 - 必须通过
setTimeout延迟初始化以确保 DOM 就绪
类型定义
组件 Props 类型约束:
interface Props {
options: EChartsOption; // 必选配置项
width?: string | number; // 默认 '100%'
height?: string | number; // 默认 '500px'
}
events.ts
用于绑定的事件
export const EVENTS = [
"legendselectchanged",
"legendselected",
"legendunselected",
"legendscroll",
"datazoom",
"datarangeselected",
"timelinechanged",
"timelineplaychanged",
"restore",
"dataviewchanged",
"magictypechanged",
"geoselectchanged",
"geoselected",
"geounselected",
"pieselectchanged",
"pieselected",
"pieunselected",
"mapselectchanged",
"mapselected",
"mapunselected",
"axisareaselected",
"focusnodeadjacency",
"unfocusnodeadjacency",
"brush",
"brushselected",
"rendered",
"finished",
"click",
"dblclick",
"mouseover",
"mouseout",
"mousemove",
"mousedown",
"mouseup",
"globalout",
"contextmenu",
"showTip",
"hideTip",
]
plugins/echarts
import * as echarts from 'echarts/core'
import {
BarChart,
LineChart,
PieChart,
MapChart,
PictorialBarChart,
RadarChart,
ScatterChart,
CustomChart,
HeatmapChart,
GaugeChart,
} from 'echarts/charts'
import {
TitleComponent,
TooltipComponent,
GridComponent,
PolarComponent,
AriaComponent,
ParallelComponent,
LegendComponent,
DataZoomComponent,
VisualMapComponent,
ToolboxComponent ,
} from 'echarts/components'
import { CanvasRenderer } from 'echarts/renderers'
echarts.use([
LegendComponent,
TitleComponent,
TooltipComponent,
GridComponent,
PolarComponent,
AriaComponent,
ParallelComponent,
BarChart,
LineChart,
PieChart,
MapChart,
GaugeChart,
CanvasRenderer,
PictorialBarChart,
RadarChart,
ScatterChart,
CustomChart,
HeatmapChart ,
DataZoomComponent,
VisualMapComponent,
ToolboxComponent
])
export default echarts
Echart.vue
<script setup lang="ts">
import type { EChartsOption } from "echarts";
import echarts from "@/plugins/echarts";
import { debounce } from "lodash-es";
import "echarts-wordcloud";
import { propTypes } from "@/utils/propTypes";
import {
computed,
PropType,
ref,
unref,
watch,
onMounted,
onBeforeUnmount,
onActivated,
} from "vue";
import { isString } from "@/utils/is";
import { EVENTS } from "./events";
const emits = defineEmits(EVENTS);
const props = defineProps({
options: {
type: Object as PropType<EChartsOption>,
required: true,
},
width: propTypes.oneOfType([Number, String]).def("100%"),
height: propTypes.oneOfType([Number, String]).def("500px"),
});
const elRef = ref<ElRef>();
let echartRef: Nullable<echarts.ECharts> = null;
const contentEl = ref<Element>();
const styles = computed(() => {
const width = isString(props.width) ? props.width : `${props.width}px`;
const height = isString(props.height) ? props.height : `${props.height}px`;
return {
width,
height,
};
});
const initChart = () => {
if (unref(elRef) && props.options) {
echartRef = echarts.init(unref(elRef) as HTMLElement);
echartRef?.setOption(unref(options));
EVENTS.forEach((event) => {
echartRef?.on(event, (params) => {
emits(event, params);
});
});
}
};
watch(
() => options.value,
(options) => {
if (echartRef) {
echartRef?.setOption(options);
}
},
{
deep: true,
}
);
const resizeHandler = debounce(() => {
if (echartRef) {
echartRef.resize();
}
}, 100);
const contentResizeHandler = async (e: TransitionEvent) => {
if (e.propertyName === "width") {
resizeHandler();
}
};
onMounted(() => {
setTimeout(() => {
initChart();
}, 0);
window.addEventListener("resize", resizeHandler);
});
onBeforeUnmount(() => {
window.removeEventListener("resize", resizeHandler);
});
onActivated(() => {
if (echartRef) {
echartRef.resize();
}
});
</script>
<template>
<div ref="elRef" :class="[$attrs.class]" :style="styles"></div>
</template>











