按需加载Echarts组件封装

538 阅读1分钟

封装v-echarts

按需加载重点: 通过 echartsComponents 参数接受Echarts组件

<template>
  <div class="v-echarts">
    <div ref="chartRef" style="width: 100%; height: 100%; margin: 0 auto;"></div>
    <el-empty class="echarts-empty" v-if="isEmpty"></el-empty>
  </div>
</template>

<script lang="ts">
import { defineComponent, nextTick, onUnmounted, ref } from 'vue'
import { downloadFile } from '@/utils'

import * as echarts from 'echarts/core'
import { GridComponent, TooltipComponent } from 'echarts/components'
import { CanvasRenderer } from 'echarts/renderers'
echarts.use([GridComponent, TooltipComponent, CanvasRenderer])
export default defineComponent({
  name: 'v-echarts',
  props: {
    echartsComponents: {
      type: Array,
      default: () => []
    },
    option: {
      type: Object,
      default: () => ({})
    },
    isEmpty: {
      type: Boolean,
      default: false
    }
  } as any,
  setup (props) {
    echarts.use(props.echartsComponents)
    
    const resizeChart = (el:any, myChart:any) => {
      const myObserver:any = new ResizeObserver(() => {
        const width = el.parentElement.offsetWidth * 0.9
        if (myChart) myChart.resize({ width: el.offsetWidth > width ? el.offsetWidth : width })
      })
      myObserver.observe(el)
      return myObserver
    }
    
    const chartRef = ref()
    let myChart: any = null // echarts 实例

    let observer: any = null // observer 实例
    const clear = () => {
      if (myChart) {
        myChart.dispatchAction({ type: 'hideTip' }) // 清除tootip
        myChart.dispose() // 清空当前实例
      }
      if (observer) observer.disconnect()
    }

    // 初始化 echarts
    const init = (option = {}) => {
      clear()
      nextTick(() => {
        const el = chartRef.value
        myChart = echarts.init(el)
        setOption(option)
        observer = resizeChart(el, myChart)
      })
    }

    const setOption = (option = {} as any) => myChart && myChart.setOption({
      backgroundColor: '#ffffff', // 背景色
      series: [],
      grid: { left: '80', right: '80', top: '40', bottom: '40'},
      xAxis: { type: 'category', data: [] },
      yAxis: { type: 'value' },
      ...option,
    }, true)

    onUnmounted(() => {
      clear()
    })
    return {
      chartRef,
      init,
      setOption,
      download (name = '下载') {
        downloadFile(myChart.getDataURL(), name)
      }
    }
  }
})
</script>

<style lang="scss" scoped>
.v-echarts {
  position: relative;
  width: 100%;
  height: 100%;
  .echarts-empty {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    left: 0;
    width: 100%;
    height: 100%;
    background: #fff;
  }
}
</style>

组件使用

业务组件只需关注 option 及组件的初始化和渲染

<template>
    <v-echarts
      v-loading="loading"
      ref="vEchartsRef"
      style="height: 330px;"
      :echartsComponents="echartsComponents"
      :isEmpty="echartsData.length === 0"
    ></v-echarts>
</template>

<script lang="ts">
import { defineComponent, ref, onMounted } from 'vue'

import vEcharts from '@/components/v-echarts.vue'

import { LineChart } from 'echarts/charts'
import { LegendComponent } from 'echarts/components'
export default defineComponent({
  name: 'trend-chart',
  components: { vEcharts },
  setup (props) {
    const vEchartsRef = ref()
    
    const option = {
        color: ['#50B1FC', '#50D1A6', '#F2B63F'],
        xAxis: {
            type: "category",
            data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
        },
        yAxis: {},
        series: [{
            data: [],
            type: "line"
        }]
    }

    const renderChart = () => {
      option.series[0].data = [820, 932, 901, 934, 1290, 1330, 1320]
      vEchartsRef.value.setOption(option)
    }

    const init = () => {
      vEchartsRef.value.init(option)
    }
    

    onMounted(()=> {
      init()
    })
    
    return {
      init,
      renderChart,
      echartsComponents: [LineChart, LegendComponent]
    }
  }
})
</script>

<style lang="scss" scoped></style>