vue3-echarts实现封装

327 阅读2分钟

背景

echarts是一种数据可视化方案,通过图形可以直观,简洁的呈现我们要展示的数据,是我们数据可视化中开发中必不可少的一种方案,相信大家都有用过这个东西,也有一些自己的开发方案,现在我想跟大家介绍一下我的封装方案,希望可以跟大家一起讨论进步一下。 我的封装解决了以下几个问题

  1. 解决不断去写初始化echarts实例的代码
  2. 解决父元素从隐藏到展示后,echarts只有100px宽高的问题
  3. 解决窗口变化后,echarts不能自适应问题
  4. 只处理options相关代码,可以允许使用者在基础上,封装项目中特有的,共同的echart的实现

image.png

1.安装包

yarn add echarts -S  // 安装echarts包
yarn add resize-observer-polyfill -S  // 基于 MutationObserver 实现的一个可以观察窗口(div元素)宽高变化的包

2.代码封装

<script lang="ts">
/* @description
 * @fileName EchartRender.vue
 * @author maqianbo-web
 * @date 2023/10/07 10:35:39
 * @version
 */
export default {
    name: 'EchartRender',
};
</script>
<script setup lang="ts">
import { ref, onMounted, nextTick, watch } from 'vue';
import * as echarts from 'echarts';
import { ECharts } from 'echarts';
import ResizeObserver from 'resize-observer-polyfill';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const prop = defineProps<{ echartOptions: any }>();
const emit = defineEmits<{
    (e: 'instance', value: ECharts): void;
    (e: 'click', value: echarts.ECElementEvent): void;
}>();
const echartWrap = ref<HTMLElement | null>(null);
let echartInstance: ECharts | null = null;
let resizeObserver: ResizeObserver | null = null;

onMounted(() => {
    initRender();
});
watch(
    prop.echartOptions,
    () => {
        renderEchart();
    },
    { deep: true }
);
const initRender = () => {
    nextTick(() => {
        if (!echartInstance) {
            echartInstance = echarts.init(echartWrap.value);
        }
        resizeObserver = new ResizeObserver(() => {
            window.requestAnimationFrame(() => {
                echartInstance?.resize(); // 窗口宽高变化,可以重新渲染echarts(可解决打开模态框echarts只有100px问题)
            });
        });
        resizeObserver.observe(echartWrap.value as HTMLElement);
        echartInstance.on('click', (params: echarts.ECElementEvent) => {
            emit('click', params);
        });
        emit('instance', echartInstance);
        renderEchart();
    });
};
const renderEchart = () => {
    echartInstance?.clear();
    echartInstance?.setOption(prop.echartOptions);
};
onMounted(() => {
    echartInstance?.dispose();
    resizeObserver?.unobserve(echartWrap.value as HTMLElement);
});
</script>

<template>
    <section ref="echartWrap" class="h-full flex-center"></section>
</template>

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

3.使用

<script lang="ts">
export default {
    name: 'EchartShow',
};
</script>
<script setup lang="ts">
import { onMounted, reactive } from 'vue';
import { EChartsOption } from 'echarts';
import EchartRender from './components/EchartRender.vue';

const getBarOptions: (name: string) => EChartsOption = (name) => {
    return {
        tooltip: {},
        legend: {
            data: [name],
        },
        xAxis: {
            data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子'],
        },
        yAxis: {},
        series: [
            {
                name,
                type: 'bar',
                data: [5, 20, 36, 10, 10, 20],
            },
        ],
    };
};
const getLineOptions = () => {
    return {
        tooltip: {},
        xAxis: {
            type: 'category',
            data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
        },
        yAxis: {
            type: 'value',
        },
        series: [
            {
                data: [150, 230, 224, 218, 135, 147, 260],
                type: 'line',
            },
        ],
    };
};
let data = reactive({
    barOptions: {},
    lineOptions: {},
});

onMounted(() => {
    // 可以将一些经常用的,相同的options生成方案提炼出来,使用
    let barOptions = getBarOptions('销售');
    data.barOptions = reactive(barOptions);
    let lineOptions = getLineOptions();
    data.lineOptions = lineOptions;
});
</script>

<template>
    <el-row class="wrap">
        <el-col :span="12">
            <echart-render :echart-options="data.barOptions"></echart-render>
        </el-col>
        <el-col :span="12">
            <echart-render :echart-options="data.lineOptions"></echart-render>
        </el-col>
    </el-row>
</template>

<style lang="less" scoped>
.wrap {
    height: 300px;
}
</style>

代码仓库

github.com/maqianbo-we… 需要查看的小伙伴可以自己克隆下来试一下