vue3.x对echarts的二次封装之按需加载过程详解
作者:SunnyRun!
echarts是我们后台系统中最常用的数据统计图形展示,外界对它的二次封装也不计层数,这篇文章主要介绍了vue3.x对echarts的二次封装之按需加载,需要的朋友可以参考下
效果展示
1、echarts是我们后台系统中最常用的数据统计图形展示,外界对它的二次封装也不计层数;
2、在业务代码内每次的初始dom和绑定setOption导致代码量的堆积不利于维护
3、拓展公共echarts组件,通过不同入参开启对应的功能更利于维护和排查问题
4、echarts的版本5.4.x
- 当前代码示例只引用了:BarChart, LineChart, PieChart, GaugeChart 这几种图形类型,其余类型需按需引用,在useCharts.ts内进行
1. 创建v-charts文件夹;
可自定义文件名
1.1 创建useCharts.ts 文件
该文件用作于处理和初始化echarts的公用逻辑,抽离出来使用vue3的hooks用来注水操作
1.2 创建v-charts.vue 文件
该文件用作于echarts的dom承载和相关api的入参只有少量逻辑;
2. 相关代码的处理方式:
useCharts.ts文件
import * as echarts from 'echarts/core' import { TitleComponent, LegendComponent, TooltipComponent, GridComponent, DatasetComponent, TransformComponent, ToolboxComponent, MarkAreaComponent, MarkLineComponent, MarkPointComponent } from 'echarts/components' import { BarChart, LineChart, PieChart, GaugeChart } from 'echarts/charts' import { LabelLayout, UniversalTransition } from 'echarts/features' import { CanvasRenderer } from 'echarts/renderers' import { ShallowRef, shallowRef, Ref, onBeforeUnmount, watch, useAttrs, shallowReactive } from 'vue' interface ChartHookOption { theme?: Ref<string> el: ShallowRef<HTMLElement> options: any } /** * 视口变化时echart图表自适应调整 */ class ChartsResize { #charts = new Set<echarts.ECharts>() // 缓存已经创建的图表实例 #timeId = null constructor() { window.addEventListener('resize', this.handleResize.bind(this)) // 视口变化时调整图表 } getCharts() { return [...this.#charts] } handleResize() { clearTimeout(this.#timeId) this.#timeId = setTimeout(() => { this.#charts.forEach((chart) => { chart.resize() }) }, 350) } add(chart: echarts.ECharts) { this.#charts.add(chart) } remove(chart: echarts.ECharts) { this.#charts.delete(chart) } removeListener() { window.removeEventListener('resize', this.handleResize) } } export const chartsResize = new ChartsResize() export const useCharts = ({ el, theme, options }: ChartHookOption) => { echarts.use([ BarChart, LineChart, BarChart, PieChart, GaugeChart, TitleComponent, LegendComponent, TooltipComponent, GridComponent, DatasetComponent, TransformComponent, LabelLayout, UniversalTransition, CanvasRenderer, ToolboxComponent, MarkAreaComponent, MarkLineComponent, MarkPointComponent ]) const charts = shallowRef<echarts.ECharts>() const setOptions = (opt: echarts.EChartsCoreOption) => { charts.value.setOption(opt) } const initChart = () => { charts.value = echarts.init(el.value, theme) charts.value.setOption(options) chartsResize.add(charts.value) // 将图表实例添加到缓存中 initEvent() // 添加事件支持 } // 初始化事件 const attrs = useAttrs() const initEvent = () => { Object.keys(attrs).forEach((attrKey) => { if (/^on/.test(attrKey)) { const cb = attrs[attrKey] attrKey = attrKey.replace(/^on(Chart)?/, '') attrKey = `${attrKey[0]}${attrKey.substring(1)}` typeof cb === 'function' && charts.value?.on(attrKey, cb as () => void) } }) } onBeforeUnmount(() => { chartsResize.remove(charts.value) // 移除缓存 }) return { charts, setOptions, initChart, initEvent } } export const chartsOptions = <T extends echarts.EChartsCoreOption>(option: T) => shallowReactive<T>(option)
v-charts.vue代码模块
<template> <div class="v-charts" ref="chartRef" /> </template> <script lang="ts" setup> import * as echarts from 'echarts/core' import { useCharts, chartsResize } from './useCharts' import { PropType, toRefs, shallowRef, onMounted, watch, nextTick } from 'vue' const props = defineProps({ theme: String, delay: [String, Boolean], isWatch: [String, Boolean, Object], options: { type: Object as PropType<echarts.EChartsCoreOption>, default: () => ({}) }, }) const { theme, options } = toRefs(props) const chartRef = shallowRef() const { charts, setOptions, initChart } = useCharts({ theme, el: chartRef, options }) // 开启默认放大缩放功能 const turnOndataZoom = () => { charts.value.dispatchAction({ type: 'takeGlobalCursor', key: 'dataZoomSelect', dataZoomSelectActive: true }) } onMounted(async () => { await initChart() setOptions(options.value) }) watch( options, () => { setOptions(options.value) nextTick(() => turnOndataZoom()) }, { deep: true } ) watch( () => props.isWatch, // 是否开启外部左侧菜单引起的布局适配问题 () => { chartsResize.handleResize() }, { deep: true, immediate: true } ) defineExpose({ chartRef: chartRef, $charts: charts }) </script> <script lang="ts"> export default { name: "v-charts" }; </script> <style lang="scss" scoped> .v-charts { width: 100%; height: 100%; clear: both; min-height: 360px; } </style>
3、引用组件或者全局注入
3.1 创建index.js文件
import vCharts from './v-charts/v-charts.vue' export * from './v-charts/useCharts'; const components = [vCharts]; // 可添加需要全局注入的公共组件 const install = function (Vue: any) { components.forEach((app) => { Vue.component(app.name, app); }); }; export default install;
3.2 在main.ts文件内进行引用
import CustomUi from '@/components/index' app.use(CustomUi)
4、在业务文件中的使用
<template> <v-charts ref="myCharts" :isWatch="isActiveName" :options="setOptions" /> </template> <script lang="ts" setup> import { ref, reactive, shallowRef, onBeforeMount } from 'vue' const option = { toolbox: { feature: { dataZoom: { icon: null } } }, xAxis: { data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], }, yAxis: {}, series: [ { type: "line", data: [23, 24, 18, 25, 27, 28, 25], }, ], }; const myCharts = ref(null) const isActiveName = ref<boolean>(false) const setOptions = shallowRef<Record<string, any>>({}) onBeforeMount(() => { setOptions.value = option }) </script>
5、完结
第一版的时候setOption这块的内容是在组件内部;因为我们的业务偏复杂,操作setOption内容较多又涉及到轮询处理数据结构;所以将该内容放置外了;这块可以依据自身需求而定
封装echarts没有过于封装,主要是针对按需情况和初始化绑定,自适应屏幕而定的封装
不通的需求处理方式不通,有不合理之处还请各位谅解
到此这篇关于vue3.x对echarts的二次封装之按需加载的文章就介绍到这了,更多相关vue3.x按需加载内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!