MUKI AI Summary
重構專案時,將 Apache ECharts 按需引入,僅使用 Line Chart 和 Bar Chart,並封裝相同的 options 設定。ECharts 圖表寬高需指定為 px,不能用 %。使用版本為 Apache ECharts v5.4,框架為 Vue3 + TypeScript。
可從 ECharts 官網範例查看引入元件,並提供 TypeScript 按需引入範例。封裝函式包括 setOption、resize 和 dispose,確保圖表自適應調整。使用 nextTick 等方法確保 dom 渲染完畢,並在 Vue 中使用父層寬高作為圖表寬高。...
最近在重構公司專案,想要把 Apache ECharts 套件的處理,寫得更好一些,不然每次隔一段時間回去看 code,都覺得超失憶 😂...
這次試著按需引入 ECharts 的元件,我只有用到 Line Chart 以及 Bar Chart;然後再把 Echarts 封裝起來,畢竟有很多 options 設定是一樣的,可以整在一起。
最後還有一個,我覺得很難處理的部分,就是要特別去指定 ECharts 圖表的寬高,單位必須是 px
,不能為 %
- 使用版本:Apache ECharts v5.4
- 使用框架:Vue3 + TypeScript
如何看 ECharts 引入了哪些元件
我們可以從 ECharts 提供的範例與完整語法,來看到他用了哪些元件
▼ 假設我選了一個「折線圖堆疊」,點進去之後,切換到「完整代碼」的分頁,把「按需引入」打開,就可以看到完整的 code 了。
TypeScript 的按需引入
另外如果你也是用 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 = { // ... };
封裝 ECharts
我封裝的函式如下:
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
在 Vue 中使用 ECharts
▼ 以下是完整的語法,包含使用父層的寬高當做圖表寬高,以及寫入獨立的 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 完全渲染完畢。