SoFunction
Updated on 2025-04-07

Vue3 custom Echars component comes with global configuration method

Vue3 custom Echars component comes with global configuration

<template>
  <div ref="chart"  :  :class="className" :style="{ height: height, width: width }"></div>
</template>
 
<script setup>
import { ref, reactive, toRefs, onMounted, onBeforeUnmount,watch } from 'vue'
import * as echarts from 'echarts';
 
// Pass parametersconst props = defineProps({
  className: {
      type: String,
      default: 'chart'
    },
    id: {
      type: String,
      default: 'chart'
    },
    width: {
      type: String,
      default: '100%'
    },
    height: {
      type: String,
      default: '280px'
    },
    options: {
      type: Object,
      default: () => { }
    }
})
const chart = ref(null);
 
 
const createChart = () => {
  const myChart = ();
  
    = (, 'tdTheme')
  (,true);
    //  (, true)
    ("resize",  () => {
        ()
      })
};
 
watch(() => , (options) => {
  if () {
    // Set true to clear the echart cache    (options, true)
  }
}, { deep: true })
onMounted(() => {
  createChart();
});
onBeforeUnmount(() => {
  ("resize", () => {
        ()
      })
  ()
   = null
})
</script>
import Echart from './components/echart/'
import echarts from 'echarts'
const app = createApp(App)
 
// Mount echarts to global object.$echarts = echarts
 
// Register global components('Echart', Echart)

Case

<template>
  <div ref="chart" style="width: 600px; height: 400px;"></div>
</template>
 
<script setup>
import { ref, onMounted } from 'vue';
import * as echarts from 'echarts';
 
const chart = ref(null);
 
const createChart = () => {
  const myChart = ();
  ({
    xAxis: {
      type: 'category',
      data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    },
    yAxis: {
      type: 'value'
    },
    series: [{
      data: [120, 200, 150, 80, 70, 110, 130],
      type: 'bar'
    }]
  });
};
 
onMounted(() => {
  createChart();
});
</script>

vue3 encapsulation echarts component

During the development process, you need to encapsulate the echarts component, which is convenient for multiple use. The following is the detailed process of encapsulation.

Pre-action: Install echarts and resize-observer-polyfill plug-in

  • Create a new file
import * as echarts from "echarts/core";
/**
  * The chart types used are introduced as needed here
  */
import { BarChart, LineChart, type LineSeriesOption, PieChart, type PieSeriesOption } from "echarts/charts";
/**
  * The configuration used is introduced as needed here
  * In particular, including MarkPoint markLine, etc., should also be used separately, otherwise it will not be displayed in the chart.
  */
import {
  LegendComponent,
  type LegendComponentOption,
  TitleComponent,
  //The definition of component types is ComponentOption  type TitleComponentOption,
  TooltipComponent,
  type TooltipComponentOption,
  GridComponent,
  type GridComponentOption,
  DataZoomComponent,
  type DataZoomComponentOption,
  // Dataset Component  DatasetComponent,
  type DatasetComponentOption,
  // Mark components  MarkAreaComponent,
  type MarkAreaComponentOption,
  MarkLineComponent,
  type MarkLineComponentOption,
  MarkPointComponent,
  type MarkPointComponentOption,
  // Built-in data converter component (filter, sort)  TransformComponent,
} from "echarts/components";

import { LabelLayout, UniversalTransition } from "echarts/features";
import { CanvasRenderer } from "echarts/renderers";

import { ref, shallowRef, onMounted, onBeforeUnmount } from "vue";
import ResizeObserver from "resize-observer-polyfill";
import throttle from "lodash/throttle";

/**
  * The introduced configuration uses ComposeOption to combine an Option type with only required components and charts.
  */
export type ECOption = &lt;
  | LineSeriesOption
  | PieSeriesOption
  | LegendComponentOption
  | TitleComponentOption
  | TooltipComponentOption
  | DataZoomComponentOption
  | GridComponentOption
  | DatasetComponentOption
  | MarkAreaComponentOption
  | MarkLineComponentOption
  | MarkPointComponentOption
&gt;;

/**
  * Register components introduced on demand
  */
([
  LegendComponent,
  TitleComponent,
  TooltipComponent,
  GridComponent,
  DataZoomComponent,
  DatasetComponent,
  MarkAreaComponent,
  MarkLineComponent,
  MarkPointComponent,
  TransformComponent,
  BarChart,
  LineChart,
  PieChart,
  LabelLayout,
  UniversalTransition,
  CanvasRenderer,
]);

export default function useChart() {
  const canvasEl = shallowRef();
  const myChart = shallowRef();
  /**
    * Monitor container width changes or browser window changes to automatically display echarts charts
    * The reason why you don't use ("resize", resizeFn) is that this method only listens to the browser window without changing
    * -No monitoring of the size changes of the container you are in. It is difficult to meet the needs of the development scenario
    */
  const resizeObserver = ref&lt;ResizeObserver&gt;();
  const bindResize = () =&gt; {
    const deboundResize = throttle(() =&gt; {
      ?.resize();
    }, 500);

     = new ResizeObserver(() =&gt; {
      deboundResize();
      // ?.resize();
    });
    ();
  };
  // Unbind resize  const unbindResize = () =&gt; {
    ?.unobserve();
  };

  onMounted(() =&gt; {
    /**
      * Register echarts and enable monitoring container size changes
      */
     = ();
    bindResize();
  });

  onBeforeUnmount(() =&gt; {
    /**
      * Destroy listening and echarts
      */
    unbindResize();
    ?.dispose();
     = null;
  });

  return {
    myChart,
    canvasEl,
  };
}

  • Page encapsulation
&lt;template&gt;
  &lt;div&gt;
    &lt;div class="chart-container" :style="containerStyle"&gt;
      &lt;div v-show="dataEmptyFlag" class="chart-empty"&gt;
        &lt;span class="empty-title"&gt;No data yet&lt;/span&gt;
      &lt;/div&gt;
      &lt;div ref="canvasEl" :style="containerStyle" /&gt;
      &lt;div v-show="loading" class="chart-loading"&gt;&lt;Spin /&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/template&gt;
&lt;script setup lang="ts"&gt;
import { Spin } from "ant-design-vue";
import { ref, watch } from "vue";
import useChart from "./useECharts";
import type { ECOption } from "./useECharts";

interface ChartProps {
  containerStyle?: any;
  loading?: boolean;
  dataEmptyFlag?: boolean;
  options?: ECOption;
}

const props = withDefaults(defineProps&lt;ChartProps&gt;(), {
  containerStyle: {
    height: "990px",
    width: "100%",
  },
  loading: false,
  dataEmptyFlag: false,
});

const { myChart, canvasEl } = useChart();

watch(
  () =&gt; props?.options,
  (cur) =&gt; {
    if (myChart) {
      /**
        * Clear the last data before drawing the chart here. Otherwise, data residues sometimes occur.
        */
      ();
      (cur);
    }
  },
);
&lt;/script&gt;
&lt;style scoped&gt;
.chart-container {
  position: relative;
  width: 100%;
}
.chart-loading {
  align-items: center;
  background-color: #ffffff;
  bottom: 0;
  display: flex;
  justify-content: center;
  left: 0;
  position: absolute;
  right: 0;
  top: 0;
  z-index: 1999;
}
.chart-empty {
  position: absolute;
  z-index: 1;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  color: #888;
  font-size: 14px;
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}
.empty-title {
  font-size: 16px;
  font-weight: 500;
  color: #000;
}
&lt;/style&gt;
  • Component call
/**
  * Include corresponding parameters and chart configuration items
  */
&lt;anewCharts :loading="loading" :data-empty-flag="dataEmptyFlag" :containerStyle="chartStyle" :options="chartOptions"&gt;&lt;/anewCharts&gt;

Summarize

The above is personal experience. I hope you can give you a reference and I hope you can support me more.