vue3.x對echarts的二次封裝之按需加載過程詳解
效果展示
1、echarts是我們后臺系統(tǒng)中最常用的數據統(tǒng)計圖形展示,外界對它的二次封裝也不計層數;
2、在業(yè)務代碼內每次的初始dom和綁定setOption導致代碼量的堆積不利于維護
3、拓展公共echarts組件,通過不同入參開啟對應的功能更利于維護和排查問題
4、echarts的版本5.4.x
- 當前代碼示例只引用了:BarChart, LineChart, PieChart, GaugeChart 這幾種圖形類型,其余類型需按需引用,在useCharts.ts內進行
1. 創(chuàng)建v-charts文件夾;
可自定義文件名
1.1 創(chuàng)建useCharts.ts 文件
該文件用作于處理和初始化echarts的公用邏輯,抽離出來使用vue3的hooks用來注水操作
1.2 創(chuàng)建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>() // 緩存已經創(chuàng)建的圖表實例 #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 創(chuàng)建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、在業(yè)務文件中的使用
<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這塊的內容是在組件內部;因為我們的業(yè)務偏復雜,操作setOption內容較多又涉及到輪詢處理數據結構;所以將該內容放置外了;這塊可以依據自身需求而定
封裝echarts沒有過于封裝,主要是針對按需情況和初始化綁定,自適應屏幕而定的封裝
不通的需求處理方式不通,有不合理之處還請各位諒解
到此這篇關于vue3.x對echarts的二次封裝之按需加載的文章就介紹到這了,更多相關vue3.x按需加載內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
vue3?使用provide?inject父子組件傳值失敗且子組件不響應
這篇文章主要介紹了vue3使用provide?inject父子組件傳值傳不過去且傳遞后子組件不具備響應性問題解決方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-08-08