ECharts 简介

Vue3 组件封装与按需引入

把 ECharts 封装成可复用的 Vue3 组件,处理好更新与销毁,并用按需引入减小打包体积。

🎯 引言

前面每画一个图都要写一遍 init、resize、卸载清理,重复又容易漏。学会这篇文章,你能把 ECharts 封装成一个通用的 Vue3 组件,一行代码画出任意图表;还会用按需引入把打包体积降下来,完成从“会用”到“用好”的最后一跃。


🧱 为什么要封装

直接在业务组件里用 ECharts 有三个痛点:

  • 重复代码:init、setOption、resize 监听,每个图表都要写一遍。
  • 容易内存泄漏:组件销毁时忘记 dispose(),图表实例一直占着内存。
  • 写法不统一:团队里每个人写一套,维护成本高。

封装的目标:业务代码只关心 option,其余全部收进组件。


🛠 封装 BaseChart 组件

components/BaseChart.vue
<template>
    <div ref="chartRef" class="chart"></div>
</template>

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

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

const chartRef = ref(null);
let chart = null;

const handleResize = () => {
    chart && chart.resize();
};

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

// option 变化时自动更新图表
watch(
    () => props.option,
    (newOption) => {
        chart && chart.setOption(newOption);
    },
    { deep: true }
);

onBeforeUnmount(() => {
    window.removeEventListener('resize', handleResize);
    chart && chart.dispose();
    chart = null;
});
</script>

<style scoped>
.chart {
    width: 100%;
    height: 400px;
}
</style>

组件做了四件事:挂载时初始化、option 变化时更新、窗口变化时自适应、卸载时销毁。

使用时就一行:

views/Sales.vue
<template>
    <BaseChart :option="option" />
</template>

<script setup>
import { computed } from 'vue';
import BaseChart from '@/components/BaseChart.vue';

const option = computed(() => ({
    title: { text: '一周销量' },
    tooltip: { trigger: 'axis' },
    xAxis: {
        type: 'category',
        data: ['周一', '周二', '周三', '周四', '周五'],
    },
    yAxis: { type: 'value' },
    series: [
        {
            name: '销量',
            type: 'bar',
            data: [120, 200, 150, 80, 70],
        },
    ],
}));
</script>
dispose() 一定不能漏。尤其是配合 v-if 频繁显隐图表时,不销毁的实例会越积越多,页面越来越卡。

🧰 按需引入减小体积

import * as echarts from 'echarts' 会把所有图表类型和组件都打包进来,体积不小。如果项目只用几种图,可以按需引入:

utils/echarts.js
import * as echarts from 'echarts/core';

// 只引入用到的图表
import { BarChart, LineChart, PieChart } from 'echarts/charts';

// 只引入用到的组件
import {
    GridComponent,
    LegendComponent,
    TitleComponent,
    TooltipComponent,
} from 'echarts/components';

// 渲染器
import { CanvasRenderer } from 'echarts/renderers';

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

export default echarts;

然后把 BaseChart.vue 里的引入换成这个模块:

components/BaseChart.vue
import echarts from '@/utils/echarts.js';
按需引入是“用到什么注册什么”。哪天图表渲染不出来或报模块不存在的错,先检查是不是忘了在 echarts.use() 里注册对应模块。

💡 Vue 中使用 ECharts 的常见坑

原因对策
图表不显示容器高度为 0,或在 onMounted 前 init设好高度,在 onMounted 里初始化
页面变卡组件卸载时未 dispose封装组件统一处理销毁
窗口缩放错位没监听 resize封装组件统一处理自适应
数据变了图没变option 引用没变,watch 没触发deep: true 或返回新对象
打包体积大全量引入 echarts按需引入

🧾 小节总结

  • 封装组件把 init、更新、resize、dispose 统一收拢,业务侧只传 option
  • watch 监听 option 变化自动 setOptiondeep: true 保证深层变化也能捕获。
  • onBeforeUnmount 中移除监听并 dispose(),杜绝内存泄漏。
  • 按需引入用 echarts/core 配合 echarts.use(),只打包用到的图表和组件。
  • 常见坑:容器高 0、忘记销毁、忘记 resize、watch 不触发、全量引入。

❓ 知识问答

Q1:为什么 option 建议用 computed 包一层?

这样响应式数据变化时会生成新的 option,配合组件里的 watch,图表自动刷新,业务代码不需要手动调 setOption

Q2:社区有现成的封装吗?

有,比如 vue-echarts,思路和我们封装的完全一致。自己动手封装一遍能彻底搞懂生命周期,之后用别人的库也知道它在干什么。

Q3:按需引入后还能用 echarts.graphic.LinearGradient 吗?

可以,graphicecharts/core 的一部分,不受按需引入影响。

Q4:一个页面放多少个图表合适?

没有硬性上限,但每个实例都会占内存。十几个以内通常没问题,大屏场景几十个也常见,关键是确保销毁逻辑可靠。


🧪 小练习

基于本课的 BaseChart 组件完成一个“双图表页面”:

  1. 页面上放两个 BaseChart,一个显示柱状图、一个显示饼图。
  2. 准备一个按钮,点击后修改柱状图的数据,观察图表自动更新。
  3. utils/echarts.js 改成按需引入,只注册这两种图需要的模块。
views/Dashboard.vue
<template>
    <!-- 请在这里编写代码 -->
</template>

<script setup>
// 请在这里编写代码
</script>

🎉 恭喜你已经完整掌握 ECharts 的核心技能啦!从第一个图表到封装复用,你已经具备在真实项目中搞定数据可视化的能力,快去把枯燥的数据变成会说话的图表吧!