這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 按需導入的配置文件 配置文件這裡就不再贅述,內容都是一樣的,主打一個隨用隨取,按需導入。 import * as echarts from "echarts/core"; // 引入用到的圖表 import { LineChart, ty ...
這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
按需導入的配置文件
配置文件這裡就不再贅述,內容都是一樣的,主打一個隨用隨取,按需導入。
import * as echarts from "echarts/core"; // 引入用到的圖表 import { LineChart, type LineSeriesOption} from "echarts/charts"; // 引入提示框、數據集等組件 import { TitleComponent, TooltipComponent, GridComponent, LegendComponent, type TooltipComponentOption, type TitleComponentOption, type GridComponentOption, type LegendComponentOption } from "echarts/components"; // 引入標簽自動佈局、全局過渡動畫等特性 import { LabelLayout } from "echarts/features"; // 引入 Canvas 渲染器,必須 import { CanvasRenderer } from "echarts/renderers"; import type { ComposeOption } from "echarts/core"; // 通過 ComposeOption 來組合出一個只有必須組件和圖表的 Option 類型 export type ECOption = ComposeOption< | LineSeriesOption | GridComponentOption | TitleComponentOption | TooltipComponentOption | LegendComponentOption >; // 註冊必須的組件 echarts.use([ LineChart, TitleComponent, TooltipComponent, GridComponent, CanvasRenderer, LabelLayout, LegendComponent ]); export default echarts;
基本封裝
DOM結構和實例化
<script setup lang="ts"> import { Ref, onMounted, onBeforeUnmount } from "vue"; import { type EChartsType } from "echarts/core"; interface Props { option: ECOption; theme?: Object | string; // 主題 } const props = withDefaults(defineProps<Props>(), { theme: null }); const chartRef = ref<Ref<HTMLDivElement>>(null); const chartInstance = ref<EChartsType>(); // 繪製 const draw = () => { if (chartInstance.value) { chartInstance.value.setOption(props.option, { notMerge: true }); } }; // 初始化 const init = () => { if (!chartRef.value) return; // 校驗 Dom 節點上是否已經掛載了 ECharts 實例,只有未掛載時才初始化 chartInstance.value = echarts.getInstanceByDom(chartRef.value); if (!chartInstance.value) { chartInstance.value = echarts.init( chartRef.value, props.theme, { renderer: "canvas" } ); draw(); } }; watch(props, () => { draw(); }); onMounted(() => { init(); }); onBeforeUnmount(() => { // 容器被銷毀之後,銷毀實例,避免記憶體泄漏 chartInstance.value?.dispose(); }); </script> <template> <div id="echart" ref="chartRef" :style="{ width: '100px', height: '120px' }" /> </template>
chartRef
:當前的 DOM 節點,即 ECharts 的容器;
chartInstance
:當前 DOM 節點掛載的 ECharts 實例,可用於調用實例上的方法,註冊事件,自適應等;
draw
:用於繪製 ECharts 圖表,本質是調用實例的 setOption 方法;
init
:初始化,在此獲取 DOM 節點,掛載實例,註冊事件,並調用 draw
繪製圖表。
Cannot read properties of undefined (reading 'type')
請註意,上述代碼目前還不能正常運行,這裡會遇到第一個坑 —— 圖表無法顯示,這是 React 中沒有碰到的:
出現這種問題是因為,我們使用 ref
接收了 echarts.init
的實例。這會導致 chartInstance
被代理成為響應式對象,影響了 ECharts 對內部屬性的訪問。Echarts 官方 FAQ 也闡述了該問題:
所以,我們有兩種解決方法:
- 使用
shallowRef
替換ref
; - 使用
ref
+markRaw
。
shallowRef 和 ref()
不同之處在於,淺層 ref 的內部值將會原樣存儲和暴露,並且不會被深層遞歸地轉為響應式。只有對 .value
的訪問是響應式的。
而 markRaw 則會將一個對象標記為不可被轉為代理。返回該對象本身。在有些值不應該是響應式的場景中,例如複雜的第三方類實例或 Vue 組件對象,這很有用。
我們這裡使用 markRaw
對 init
進行包裹:
chartInstance.value = markRaw( echarts.init( chartRef.value, props.theme, { renderer: "canvas" } ) );
視窗防抖自適應
這裡和 React 中就差不多了,主要安利一個 Vue 官方團隊維護的 hooks 庫:vueuse 。和 React 中的 ahooks 一樣,封裝了很多實用的 hooks,我們可以使用 useDebounceFn
來優化自適應函數:
import { useDebounceFn } from "@vueuse/core"; // 視窗自適應並開啟過渡動畫 const resize = () => { if (chartInstance.value) { chartInstance.value.resize({ animation: { duration: 300 } }); } }; // 自適應防抖優化 const debouncedResize = useDebounceFn(resize, 500, { maxWait: 800 }); onMounted(() => { window.addEventListener("resize", debouncedResize); }); onBeforeUnmount(() => { window.removeEventListener("resize", debouncedResize); });
額外監聽寬高
目前,圖標的大小還是寫死的,現在我們支持 props 傳遞寬高來自定義圖表大小:
interface Props { option: ECOption; theme?: Object | string; width: string; height: string; } <template> <div id="echart" ref="chartRef" :style="{ width: props.width, height: props.height }" /> </template>
請註意:在使用時,我們必須指定容器的寬高,否則無法顯示,因為圖表在繪製時會自動獲取父容器的寬高。
flex/grid 佈局下 resize 失效的問題
這個問題剛遇到著實有點蛋疼,摸了蠻久,而 bug 觸發的條件也比較奇葩,但也比較常見:
- 在父組件中,復用多個 ECharts 組件;
- 使用了 flex 或 grid 這種沒有明確給定寬高的佈局;
此時會發現:當前視窗放大,正常觸發 resize, 圖表會隨之放大。但是,此時再縮小視窗,雖然也會觸發 resize,但是圖表的大小卻縮不回來了......
一開始還以為是我封裝的寫法有問題,直到搜到了ECharts 官方的 issues 才發現原來不止我一個遇到了