2023-01-16
Vue3 使用 Vite 整合 axios 攔截器與 axios proxy
Vue
2023.01.11
最近在重構公司專案,想要把 Apache ECharts 套件的處理,寫得更好一些,不然每次隔一段時間回去看 code,都覺得超失憶 😂…
這次試著按需引入 ECharts 的元件,我只有用到 Line Chart 以及 Bar Chart;然後再把 Echarts 封裝起來,畢竟有很多 options 設定是一樣的,可以整在一起。
最後還有一個,我覺得很難處理的部分,就是要特別去指定 ECharts 圖表的寬高,單位必須是 px
,不能為 %
我們可以從 ECharts 提供的範例與完整語法,來看到他用了哪些元件
▼ 假設我選了一個「折線圖堆疊」,點進去之後,切換到「完整代碼」的分頁,把「按需引入」打開,就可以看到完整的 code 了。
另外如果你也是用 TypeScript,ECharts 有提供完整的 TypeScript 按需引入範例
import * as echarts from 'echarts/core'; import { BarChart, // 系列類型的定義後綴都為 SeriesOption BarSeriesOption, LineChart, LineSeriesOption } from 'echarts/charts'; import { TitleComponent, // 元件類型的定義後綴都為 ComponentOption TitleComponentOption, TooltipComponent, TooltipComponentOption, GridComponent, GridComponentOption, // 資料集元件 DatasetComponent, DatasetComponentOption, // 內建資料轉換器元件 (filter, sort) TransformComponent } from 'echarts/components'; import { LabelLayout, UniversalTransition } from 'echarts/features'; import { CanvasRenderer } from 'echarts/renderers'; // 通過 ComposeOption 來組合出一個只有必須元件和圖表的 Option 類型 type ECOption = echarts.ComposeOption< | BarSeriesOption | LineSeriesOption | TitleComponentOption | TooltipComponentOption | GridComponentOption | DatasetComponentOption >; // 註冊必須的元件 echarts.use([ TitleComponent, TooltipComponent, GridComponent, DatasetComponent, TransformComponent, BarChart, LineChart, LabelLayout, UniversalTransition, CanvasRenderer ]); const option: ECOption = { // ... };
我封裝的函式如下:
setOption()
:設定 ECharts 的選項 (非共用)resize()
:調整螢幕寬度時,圖表要自適應調整dispose()
:離開時要銷毀 ECharts 元件▼ 以下是完整的用 TypeScript 做的,按需引入並封裝 ECharts 函式
import * as echarts from 'echarts/core' import { ComposeOption } from 'echarts/core' import { UniversalTransition } from 'echarts/features' import { CanvasRenderer } from 'echarts/renderers' import { TitleComponent, TitleComponentOption, ToolboxComponent, ToolboxComponentOption, TooltipComponent, TooltipComponentOption, GridComponent, GridComponentOption, LegendComponent, LegendComponentOption, DatasetComponent, DatasetComponentOption, TransformComponent } from 'echarts/components' import { LineChart, LineSeriesOption, BarChart, BarSeriesOption } from 'echarts/charts' type ECOption = ComposeOption< | BarSeriesOption | LineSeriesOption | TitleComponentOption | ToolboxComponentOption | TooltipComponentOption | GridComponentOption | LegendComponentOption | DatasetComponentOption > echarts.use([ TitleComponent, ToolboxComponent, TooltipComponent, GridComponent, LegendComponent, DatasetComponent, TransformComponent, LineChart, BarChart, CanvasRenderer, UniversalTransition ]) const useChart = (element: HTMLDivElement) => { const myChart = echarts.init(element) const setOption = (optionData: any) => { const option: ECOption = { tooltip: {}, xAxis: { data: ['襯衫', '羊毛衫', '雪紡衫', '褲子', '高跟鞋', '襪子'] }, yAxis: {}, ...optionData } return myChart.setOption(option) } const resize = () => myChart.resize() return { setOption, resize } } export default useChart
▼ 以下是完整的語法,包含使用父層的寬高當做圖表寬高,以及寫入獨立的 option 設定
<template> <div class="w-full h-full" ref="containerRef"> <div ref="chartRef"></div> </div> </template> <script setup lang="ts"> import { ref, onMounted, onUnmounted, nextTick, computed } from 'vue' import useChart from '@/plugins/EChart' const parentHeight = ref(0) const containerRef = ref<HTMLDivElement>() const chartRef = ref<HTMLDivElement>() const computedParentHeight = computed({ get () { return parentHeight.value }, set (val: number) { parentHeight.value = val; (chartRef.value as any).style.height = `${val}px` } }) let chartInstance: ReturnType<typeof useChart> onMounted(() => { nextTick(() => { setTimeout(() => { parentHeight.value = (containerRef.value as HTMLDivElement).clientHeight computedParentHeight.value = parentHeight.value chartInstance = useChart(chartRef.value as HTMLDivElement) const { setOption, resize } = chartInstance setOption({ series: [ { name: '銷量', type: 'bar', data: [5, 20, 36, 10, 10, 20] } ] }) window.addEventListener('resize', () => { if (!chartInstance) return chartInstance.resize() }) }, 1000) }) }) onUnmounted(() => { const { resize } = chartInstance window.removeEventListener('resize', () => { resize() }) }) </script>
基本上,都是使用 nextTick()
等 dom 渲染完。
但我的 dom 是用另外一個套件產生的,所以才在裡面加了 setTimeout()
,多一點等待時間,讓 dom 完全渲染完畢。