如何优雅的在项目中使用echarts

可视化对于现在管理系统开发还是挺常见的,基本都会涉及一些可视化图表的展示。一般都是通过echarts去开发的。

下面我们就来简单对 echarts 进行封装并举一些例子来带你快速开发可视化图表。

一般我们会创建一个配置文件引入当前项目中使用的图表类型,并进行注册。可以结合echarts 官网看这里

简单封装

js 复制代码
import * as echarts from "echarts/core";

import {
  BarChart,
  LineChart,
  LinesChart,
  MapChart,
  PieChart,
  EffectScatterChart,
  ScatterChart,
  GraphChart,
} from "echarts/charts";

import {
  TitleComponent,
  TooltipComponent,
  GridComponent,
  LegendComponent,
  ToolboxComponent,
  MarkLineComponent,
  VisualMapComponent,
  GeoComponent,
} from "echarts/components";

export type ECharts = echarts.ECharts;

import { CanvasRenderer } from "echarts/renderers";

// 进行注册
echarts.use([
  CanvasRenderer,
  PieChart,
  BarChart,
  LineChart,
  LinesChart,
  MapChart,
  EffectScatterChart,
  ScatterChart,
  LegendComponent,
  TitleComponent,
  TooltipComponent,
  GridComponent,
  ToolboxComponent,
  MarkLineComponent,
  VisualMapComponent,
  GeoComponent,
  GraphChart,
]);

写一个使用图表的 hooks。大致的步骤如下:

  • 我们可以通过 computed api 去缓存当前 echarts 配置。
  • 通过echarts.init去初始化图表实例,并做响应式监听,当窗口改变,使用实例的resize方法做图表响应式。
  • 提供一个设置图表 options 的配置方法,可以先设置图表配置缓存,当整个组件加载完成再去渲染整个图表。
  • 如果项目做主题切换,菜单折叠功能,我们还需要做些适配。这些状态都是全局的,所以通过 watch 监听对应变量就可以了。
js 复制代码
watch(
    () => themeStore,
    (theme) => {
      if (chartInstance) {
        // 丢弃图表实例,重新创建
        chartInstance.dispose();
        initCharts(theme as "default");
        setOptions(cacheOptions.value);
      }
    }
  );

  watch(() => collapseStore, () => {
    setTimeout(() => {
      resize();
    }, 200);
  });
  • 最后组件卸载移除一些状态。

大致代码如下:

js 复制代码
import type { EChartsOption } from "echarts";
import type { Ref } from "vue";
import { unref, nextTick, watch, computed, ref, onUnmounted } from "vue";
import echarts from "./echarts";

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

// 防抖
function deBounceFn(fn, delay = 200) {
  return function () {
    let timer = null;
    if (timer) {
      clearTimeout(timer);
    }

    setTimeout(() => {
      fn();
    }, delay);
  };
}

export function useECharts(
  elRef: Ref<HTMLDivElement>,
  theme: "light" | "dark" | "default" = "default"
) {
  // 图表实例
  let chartInstance: echarts.ECharts | null = null;
  // 响应式执行函数
  let resizeFn: Fn = resize;
  const cacheOptions = ref({}) as Ref<EChartsOption>;
  let removeResizeFn: Fn = () => {};

  resizeFn = deBounceFn(resize, 200);

  // 缓存配置
  const getOptions = computed(() => {
    if (theme !== "dark") {
      return cacheOptions.value as EChartsOption;
    }
    return {
      backgroundColor: "transparent",
      ...cacheOptions.value,
    } as EChartsOption;
  });

  // 初始化图表配置
  function initCharts(t = theme) {
    const el = unref(elRef);
    if (!el || !unref(el)) {
      return;
    }

    chartInstance = echarts.init(el, t);
    window.addEventListener("resize", resizeFn);
    const removeEvent = () => {
      window.removeEventListener("resize", resizeFn);
    };
    removeResizeFn = removeEvent;
  }

  // 设置echart options
  function setOptions(options: EChartsOption, clear = false) {
    cacheOptions.value = options;
    if (unref(elRef)?.offsetHeight === 0) {
      setTimeout(() => {
        setOptions(unref(getOptions));
      }, 30);
      return;
    }
    nextTick(() => {
      setTimeout(() => {
        // 如果未初始化先初始化
        if (!chartInstance) {
          initCharts("default");

          if (!chartInstance) return;
        }
        clear && chartInstance?.clear();
        chartInstance?.setOption(unref(getOptions), true);
      }, 30);
    });
  }

  // 图表响应式处理
  function resize() {
    chartInstance?.resize({
      animation: {
        duration: 300,
        easing: "quadraticIn",
      },
    });
  }

  // TODO: 主题改变,菜单折叠,做响应式处理
  // watch(
  //   () => themeStore,
  //   (theme) => {
  //     if (chartInstance) {
  //       // 丢弃图表实例,重新创建
  //       chartInstance.dispose();
  //       initCharts(theme as "default");
  //       setOptions(cacheOptions.value);
  //     }
  //   }
  // );

  // watch(() => collapseStore, () => {
  //   setTimeout(() => {
  //     resize();
  //   }, 200);
  // });

  // 组件卸载移除一些状态
  onUnmounted(() => {
    if (!chartInstance) return;
    removeResizeFn();
    chartInstance.dispose();
    chartInstance = null;
  });

  function getInstance(): echarts.ECharts | null {
    if (!chartInstance) {
      initCharts("default");
    }
    return chartInstance;
  }

  return {
    setOptions,
    resize,
    echarts,
    getInstance,
  };
}

一些图表的简单 demo

在介绍 echarts 图标之前,我们先来看数据如何和配置对象绑定,从而让代码看起来更舒服。

我们知道 echarts 渲染出一个图表需要设置很多配置,对于正常的程序员来说是不会将这个配置放在代码逻辑里面的。我们会提供一个配置文件将通过配置放在该文件中。在代码逻辑想办法去改变那些"变量"。

  • 方式一:封装成函数,在外部调用传参。
  • 方式二:直接提供配置对象,让外部获取属性的方式为其赋值。

折线与柱状图

具体的配置请查看源代码

js 复制代码
<template>
  <div class="echarts" ref="charts"></div>
</template>

<script lang="ts" setup>
import { onMounted, ref } from "vue";
import { lineBarEchartOption } from "./demo.data";
import { useECharts } from "./useSimpleEcharts";
const charts = ref(null);
const { setOptions } = useECharts(charts);

onMounted(() => {
  setOptions(lineBarEchartOption);
});
</script>

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

折线与折线图

具体的配置请查看源代码

js 复制代码
<template>
  <div class="echarts" ref="charts"></div>
</template>

<script lang="ts" setup>
import { onMounted, ref } from "vue";
import { DLineEchartOptionFactory } from "./demo.data";
import { useECharts } from "./useSimpleEcharts";
const charts = ref(null);
const { setOptions } = useECharts(charts);

onMounted(() => {
  const chartOption = DLineEchartOptionFactory({
    tooltip: {
      trigger: "axis",
      confine: true,
      appendToBody: false,
      valueFormatter: (value) => `${value}%`,
    },
    yAxis: [
      {
        axisLabel: {
          formatter: "{value}%",
        },
      },
    ],
    series: [
      {
        data: ["92", "52", "126", "125", "141", "64", "60", "36", "82", "45"],
      },
      {
        data: [
          "20.8",
          "20.5",
          "113.1",
          "86.1",
          "62.2",
          "42.2",
          "82.4",
          "74.5",
          "91.5",
          "54.4",
        ],
      },
    ],
  });
  setOptions(chartOption);
});
</script>

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

柱状与柱状图

具体的配置请查看源代码

js 复制代码
<template>
  <div class="echarts" ref="charts"></div>
</template>

<script lang="ts" setup>
import { onMounted, ref } from "vue";
import { DBarChartOption } from "./demo.data";
import { useECharts } from "./useSimpleEcharts";
const charts = ref(null);
const { setOptions } = useECharts(charts);

onMounted(() => {
  setOptions(DBarChartOption);
});
</script>

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

饼图

具体的配置请查看源代码

js 复制代码
<template>
  <div class="echarts" ref="charts"></div>
</template>

<script lang="ts" setup>
import { onMounted, ref } from "vue";
import { productPredictEchartOption } from "./demo.data";
import { useECharts } from "./useSimpleEcharts";
const charts = ref(null);
const { setOptions } = useECharts(charts);

onMounted(() => {
  const rich = {
    name: {
      color: "#4E5969",
      fontSize: 12,
      padding: [3, 0, 3, 3],
    },
    value: {
      color: "#4E5666",
      fontSize: 18,
      padding: [0, 0, 0, 3],
    },
    labelName: {
      color: "#4E5666",
      align: "center",
      fontSize: 12,
    },
    percent: {
      color: "#4E5666",
      fontSize: 12,
      align: "center",
      padding: [3, 0],
    },
  };
  const data = [
    {
      name: "demo1",
      value: 20,
    },
    {
      name: "demo2",
      value: 930,
    },
    {
      name: "demo3",
      value: 245,
    },
    {
      name: "demo4",
      value: 10000,
    },
    {
      name: "demo5",
      value: 2434,
    },
    {
      name: "demo6",
      value: 1256,
    },
  ];
  setOptions(productPredictEchartOption(data, rich));
});
</script>

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

如果你只是简单实现这些图表,可以直接去代码仓库粘贴复制就行,可以快速搭建一个图表,只需要赋值对应的数据即可。

其他图表,可以参考一些echarts的demo,也可快速开发。

往期文章

专栏文章

最近也在学习nestjs,有一起小伙伴的@我哦。

已辞职,虽然丢了应届生身份,但是看清了一些人的嘴脸,技术还是被很多人认可的。 🎇🎉✨

相关推荐
Jiaberrr11 分钟前
前端实战:使用JS和Canvas实现运算图形验证码(uniapp、微信小程序同样可用)
前端·javascript·vue.js·微信小程序·uni-app
everyStudy35 分钟前
JS中判断字符串中是否包含指定字符
开发语言·前端·javascript
城南云小白35 分钟前
web基础+http协议+httpd详细配置
前端·网络协议·http
前端小趴菜、36 分钟前
Web Worker 简单使用
前端
web_learning_32139 分钟前
信息收集常用指令
前端·搜索引擎
tabzzz1 小时前
Webpack 概念速通:从入门到掌握构建工具的精髓
前端·webpack
200不是二百1 小时前
Vuex详解
前端·javascript·vue.js
滔滔不绝tao1 小时前
自动化测试常用函数
前端·css·html5
码爸2 小时前
flink doris批量sink
java·前端·flink
深情废杨杨2 小时前
前端vue-父传子
前端·javascript·vue.js