一、封装组件使用
1.基础组件封装
/components/BaseChart.vue
<template>
<div ref="chartContainer" :style="{ width: props.width, height: props.height }" class="base-chart"></div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue'
import * as echarts from 'echarts'
const props = defineProps({
// 初始化额外配置
initOpts: {
type: Object,
default: () => ({})
},
// 图表配置
option: {
type: Object,
required: true,
default: () => ({})
},
width: {
type: String,
default: '100%'
},
height: {
type: String,
default: '400px'
},
theme: {
type: [String, Object],
default: 'light'
},
animation: {
type: Boolean,
default: true
}
})
const emit = defineEmits(['chart-init', 'chart-ready', 'chart-click', 'chart-dblclick'])
const chartContainer = ref(null)
let chartInstance: echarts.ECharts | null = null
// 初始化图表
const initChart = () => {
if (!chartContainer.value) return
// 销毁旧实例
if (chartInstance) {
chartInstance.dispose()
}
try {
// 创建新实例
chartInstance = echarts.init(chartContainer.value, props.theme, {
renderer: 'canvas', // 'svg' 或 'canvas'
...props.initOpts
})
emit('chart-init', chartInstance)
// 设置配置项
chartInstance.setOption(props.option, {
notMerge: false, // 是否不跟之前设置的 option 进行合并
lazyUpdate: false // 在设置完 option 后是否不立即更新图表
})
// 绑定事件
bindChartEvents()
emit('chart-ready', chartInstance)
} catch (error) {
console.error('ECharts init error:', error)
}
}
// 绑定图表事件
const bindChartEvents = () => {
if (!chartInstance) return
// 移除旧监听器
chartInstance.off('click')
chartInstance.off('dblclick')
chartInstance.on('click', (params: echarts.ECElementEvent) => {
emit('chart-click', params)
})
chartInstance.on('dblclick', (params: echarts.ECElementEvent) => {
emit('chart-dblclick', params)
})
}
// 更新图表
const updateChart = () => {
if (!chartInstance) return
chartInstance.setOption(props.option, true) // true 表示不合并旧配置
}
// 响应窗口大小变化
const resizeChart = () => {
if (!chartInstance) return
chartInstance.resize()
}
// 销毁图表
const disposeChart = () => {
if (!chartInstance) return
chartInstance.dispose()
chartInstance = null
}
// 获取图表实例
const getInstance = () => chartInstance
// 监听属性变化
watch(
() => props.option,
() => {
updateChart()
},
{ deep: true }
)
watch(
() => props.theme,
() => {
initChart()
}
)
// 监听容器尺寸变化
const resizeObserver = new ResizeObserver(() => {
resizeChart()
})
onMounted(() => {
nextTick(() => {
initChart()
})
if (chartContainer.value) {
resizeObserver.observe(chartContainer.value)
}
})
onUnmounted(() => {
disposeChart()
resizeObserver.disconnect()
})
defineExpose({
getInstance,
resize: resizeChart,
dispose: disposeChart,
update: updateChart
})
</script>
<style scoped lang="scss">
.base-chart {
display: block;
}
</style>
2.组件使用
通过给BaseChart组件传入option值,即可轻松使用啦
<template>
<div style="width: 600px">
<BaseChart :option="chartOption" :height="'400px'" />
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import BaseChart from '@/components/BaseChart.vue'
// 图表配置
const chartOption = ref({
title: {
text: '销售统计'
},
tooltip: {},
xAxis: {
data: ['一月', '二月', '三月', '四月', '五月', '六月']
},
yAxis: {},
series: [
{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}
]
})
// 图表事件处理
const handleChartReady = chartInstance => {
console.log('图表准备好了', chartInstance)
}
const handleChartClick = params => {
console.log('点击了图表', params)
}
</script>
二、封装hooks使用
1.创建 hooks
src/composables/useEChart.js
import { ref, onUnmounted, nextTick } from 'vue';
import * as echarts from 'echarts';
export function useEChart(containerRef, options = {}) {
const chartInstance = ref(null);
const { theme, initOpts } = options;
// 初始化图表
const initChart = () => {
if (!containerRef.value) return;
chartInstance.value = echarts.init(containerRef.value, theme, initOpts);
return chartInstance.value;
};
// 设置配置项
const setOption = (option, notMerge = false) => {
if (!chartInstance.value) return;
chartInstance.value.setOption(option, notMerge);
};
// 调整大小
const resize = () => {
if (!chartInstance.value) return;
chartInstance.value.resize();
};
// 销毁实例
const dispose = () => {
if (!chartInstance.value) return;
chartInstance.value.dispose();
chartInstance.value = null;
};
// 获取实例
const getInstance = () => chartInstance.value;
// 自动清理
onUnmounted(() => {
dispose();
});
return {
initChart,
setOption,
resize,
dispose,
getInstance
};
}
2.使用hooks
<template>
<div ref="chartContainer" style="width: 600px; height: 400px;"></div>
</template>
<script setup>
import { ref, onMounted, watch } from 'vue';
import { useEChart } from '@/composables/useEChart';
const option = ref({
title: {
text: '销售统计'
},
tooltip: {},
xAxis: {
data: ['一月', '二月', '三月', '四月', '五月', '六月']
},
yAxis: {},
series: [
{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}
]
})
const chartContainer = ref(null);
const { initChart, setOption, resize } = useEChart(chartContainer);
onMounted(() => {
initChart();
setOption(option.value);
});
watch( option, (newOption) => {
setOption(newOption, true);
});
</script>