vue3中如何更优雅的使用echarts?

echarts在vue或者react中使用存在的问题

  • 每个图表需要从头到尾写地一遍完整的option配置,这样一来的话就会显得十分的冗余
  • 在同一个项目中,其实不难发现各类图表设计十分相似,甚至是相同,因此我们没必要一直做重复的工作,去写差不多配置的option
  • 再一个就是窗口缩放时的适应问题
  • 那么在项目中如何按需引入echarts图表 减少打包体积的大小就显得尤为重要了

解决方案

  • 统一封装图表组件,统一管理图表配置,做到业务数据和echarts的 option样式配置数据分离
  • 统一解决窗口缩放时的图表的适应问题, 解决窗口缩放引起的echarts图形变形的问题
  • 按需引入echarts图表 减少打包体积的大小,(全量引入按需引入 打包后的体积减少了三分之一)

插件安装:

  • npm/cnpm element-resize-detector -S    动态监听元素尺寸变化,与window的resize事件不同,resize只能监听window的窗口尺寸变化,不能监听某个元素尺寸变化
  • echarts    图表组件库
  • lodash    主要利用其中的 merge 函数,用于将多个对象合并成一个新的对象。具体用法可参考:lodash.merge | Lodash中文文档 | Lodash中文网

封装 echarts 

将 ECharts 的配置封装到插件中,这里我们可以实现:按需引入图表、按需引入图表配置比如提示框,标题等等

然后在 main.js 里进行全局注册,就可以在需要使用的地方很方便快捷的进行使用了

其代码如下:

plugins/echarts.js

// 引入 echarts 核心模块,核心模块提供了 echarts 使用必须要的接口。
import * as echarts from "echarts/core";
// 按需引入折线图、饼状图、柱状图,图表后缀都为 Chart
import { BarChart, PieChart, LineChart } from "echarts/charts";
// 引入提示框,标题,直角坐标系,数据集,内置数据转换器组件,组件后缀都为 Component
import {
  TitleComponent,
  TooltipComponent,
  GridComponent,
  DatasetComponent,
  TransformComponent,
  LegendComponent,
} from "echarts/components";
// 标签自动布局、全局过渡动画等特性
import { LabelLayout, UniversalTransition } from "echarts/features";
// 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步
import { CanvasRenderer } from "echarts/renderers";

// 注册必须的组件
echarts.use([
  TitleComponent,
  TooltipComponent,
  GridComponent,
  DatasetComponent,
  TransformComponent,
  LegendComponent,
  BarChart,
  PieChart,
  LineChart,
  LabelLayout,
  UniversalTransition,
  CanvasRenderer,
]);
export default echarts;

main.js

import echarts from './plugins/echarts';


const app = createApp(App);

app.provide("$echarts", echarts);

这样做的优点如下:

  1. 模块化和组织性: 将 ECharts 的配置封装到插件中,使得项目中的 ECharts 相关配置和代码能够按功能模块进行组织和管理。这样做有助于提高代码的可维护性和可读性。

  2. 复用性: 将 ECharts 相关配置封装到插件中,可以在不同的组件中复用相同的配置。这样做有助于减少重复的代码编写,提高开发效率。

  3. 全局性: 将 ECharts 的配置通过 Vue 的 provide/provide API 全局挂载,使得在整个 Vue 应用中都能够方便地使用相同的 ECharts 实例。这样做有助于保持应用中 ECharts 实例的一致性,避免了在不同组件中重复创建 ECharts 实例的问题。

  4. 便捷性: 通过将 ECharts 配置封装到插件中,并通过 provide/provide API 进行全局挂载,可以在 Vue 应用的任意组件中直接使用 $echarts 实例,无需每次都单独引入和配置 ECharts。这样做简化了在 Vue 应用中使用 ECharts 的流程,提高了开发效率。

使用实例

这里以封装一个饼图为例,代码如下

封装饼图组件

在 components 目录下,新建 Echart 文件夹

在 Echart 文件夹下,新建 pie 文件夹,用来封装 pie 组件 和 存放其 option 配置

在 Echart 文件夹下,新建 color.js ,用来放置通用的颜色数组

pie/chartPie.vue

<template>
  <div ref="chartRef" class="chart"></div>
</template>
<script setup>
import { defineProps, ref, onMounted, onUnmounted, watch, inject } from "vue";
import { merge } from "lodash";
import ResizeListener from "element-resize-detector";
import { COLORSARRAY } from "../color.js";
import { BASEOPTIONS } from "./defaultOptions.js";
let echartInstance = null;
// 导入全局挂载的echarts
const echarts = inject("$echarts");

// 使用vue的refs声明dom节点,方便后续操作
const chartRef = ref(null);
const props = defineProps({
  seriesData: {
    type: Array,
    required: true,
    default: () => [],
  },
  // 需要特殊定制的配置
  extraOption: {
    type: Object,
    default: () => ({}),
  },
});
// 监控seriesData的变化,当变化时更新echart视图
watch(
  () => props.seriesData,
  (newVal) => {
    updateChartView();
  },
  {
    // immediate: true,
    deep: true,
  }
);

/**
 * 对chart元素尺寸进行监听,当发生变化时同步更新echart视图
 */
const addChartResizeListener = () => {
  const instance = ResizeListener({
    strategy: "scroll",
    callOnAdd: true,
  });
  instance.listenTo(chartRef.value, () => {
    if (chartRef.value) {
      // chartRef.value.resize();
      echartInstance.resize();
    }
  });
};
/**
 * 当窗口缩放时,echart动态调整自身大小
 */
const handleWindowResize = () => {
  if (chartRef.value) {
    echartInstance.resize();
    // chartRef.value?.resize();
  }
};
const assembleDataToOption = (seriesData) => {
  // 这部分的图例formatter取决于UI要求,如果你的项目中不需要,就可以不写formatter
  // 由于echarts版本的迭代,这里的写法也有稍许改变
  const formatter = (name) => {
    const total = props.seriesData.reduce((acc, cur) => acc + cur.value, 0);
    const data = props.seriesData.find((item) => item.name === name) || {};
    const percent = data.value
      ? `${Math.round((data.value / total) * 100)}%`
      : "0%";
    return `${name} ${percent}`;
  };

  return merge(
    {},
    BASEOPTIONS,
    { color: COLORSARRAY },
    {
      legend: { formatter },
      series: [{ data: props.seriesData }],
    },
    props.extraOption
  );
};
const updateChartView = () => {
  if (!chartRef.value) return;
  const fullOption = assembleDataToOption();
  echartInstance = echarts.init(chartRef.value);
  echartInstance.setOption(fullOption);
};
onMounted(() => {
  // echarts.init(chartRef.value)
  updateChartView();
  window.addEventListener("resize", handleWindowResize);
  addChartResizeListener();
});
onUnmounted(() => {
  window.removeEventListener("resize", handleWindowResize);
});
</script>
<style lang="scss">
.chart {
  width: 100%;
  height: 450px;
}
</style>

pie/defaultOptions.js

// 这里的数据会被深度合并
const BASEOPTIONS = {
  title: {
    text: "产品成功率",
    left: "center",
    padding: 70
  },
  tooltip: {
    trigger: "item",
  },
  legend: {
    orient: "vertical",
    left: "left",
  },
  series: [
    {
      name: "占比",
      type: "pie",
      radius: "50%",
      emphasis: {
        itemStyle: {
          shadowBlur: 10,
          shadowOffsetX: 0,
          shadowColor: "rgba(0, 0, 0, 0.5)",
        },
      },
      data: [], // 这里在使用的时候会被业务数据替换
    },
  ],
};
export { BASEOPTIONS };

pie/index.vue

<template>
  <chart-pie v-bind="$props" />
</template>
<script setup>
import ChartPie from "./chartPie.vue";
</script>

Echart/color.js

// 通用的颜色数组
const COLORSARRAY = ["#f75981", "#90e2a9", "#fe883a", "#2d90d1"];
export {COLORSARRAY};

使用方式
<chartPie :seriesData="dataList" :extraOption="extraOption"></chartPie>


import chartPie from "@/components/Echart/pie/chartPie.vue";
import { ref } from "vue";


const dataList = ref([
  { name: "对接成功", value: 0 },
  { name: "未成功", value: 0 },
]);
const extraOption = {
  color: ["#fe883a", "#2d90d1", "#f75981", "#90e2a9"],
};


// 请求,具体代码省略
if (res.data[0] && res.data[0].data) {
     dataList.value[0].value = res.data[0].data.chenggong_count || 0;
     dataList.value[1].value = res.data[0].data.shenqing_count || 0;
}

最终效果如下:

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白小白从不日白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值