這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
按需匯入的組態檔
組態檔這里就不再贅述,內容都是一樣的,主打一個隨用隨取,按需匯入,
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 = https://www.cnblogs.com/smileZAZ/archive/2023/06/19/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 = https://www.cnblogs.com/smileZAZ/archive/2023/06/19/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 才發現原來不止我一個遇到了??
我的理解是:首先,無論什么布局 echarts 取的都是 dom 的 clientWidth 和 clientHeight 作為容器寬高,其次,由于 flex、grid 這種布局可以不需要顯示地指定 width、height,這就導致 echarts 在自適應的程序中無法明確地獲取到容器的寬高,所以即便觸發了 resize 事件,但是重繪的圖表還是之前默認的寬高,
解決方案
給每個 flex-item
或 grid-item
自適應的寬或者高都設定一個最小值(我專案中的寬是自適應的,高度是固定的):
.chart-item { flex: 1; min-width: 30vh; height: 300px; }
這里不得不吐槽下,早在2017年就有人提出過這個問題,2020年終于給出了解釋,但是現在都2023了,這個問題還沒有得到解決,issues 還 open 著 ??
系結滑鼠事件
我們可以給圖表中的一些組件添加額外的互動,比如給 title 滑鼠 hover 事件等,記得在需要使用事件的組件上添加 triggerEvent: true
屬性,
我們演示滑鼠移入 title 顯示 y軸 name,滑鼠移出 title 隱藏 y軸 name 的需求:
interface Props { // 略... onm ouseover?: (...args: any[]) => any; onm ouseout?: (...args: any[]) => any; } const init = () => { // 略...... // 系結 mousehover 事件: if (props.onMouseover) { chartInstance.value.on("mouseover", (event: Object) => { props.onMouseover(event, chartInstance.value, props.option); }); } // 系結 mouseout 事件: if (props.onMouseout) { chartInstance.value.on("mouseout", (event: Object) => { props.onMouseout(event, chartInstance.value, props.option); }); } } };在上述注冊的回呼事件中,我們將 ECharts 實體和傳入的 option 重新傳出去,這樣可以就在外面重新配置 option 并呼叫實體的方法進行圖表的重繪了:
import Chart from "@/components/BaseChart/index.vue"; import type { EChartsType } from "echarts/core"; import type { ECOption } from "@/components/BaseChart/config"; import type { YAXisOption } from "echarts/types/dist/shared"; // 滑鼠移入,顯示y軸 name const onm ouseover = (chart: EChartsType, option: ECOption) => { (option.yAxis as YAXisOption).nameTextStyle.color = "#ccc"; // 重繪圖表 chart.setOption(option); }; // 滑鼠移出,隱藏y軸 name const onm ouseout = (chart: EChartsType, option: ECOption) => { (option.yAxis as YAXisOption).nameTextStyle.color = "transparent"; chart.setOption(option); }; <template> <Chart height="305px" :option="{ // 略...... title: { text: "標題", triggerEvent: true }, }" :on-mouseover="onMouseover" :on-mouseout="onMouseout" /> </template>
展示 loading 影片
支持受控的 loading 影片
interface Props { // 略... loading?: boolean; // 受控 } const props = withDefaults(defineProps<Props>(), { theme: null, loading: false }); watch( () => props.loading, loading => { loading ? chartInstance.value.showLoading() : chartInstance.value.hideLoading(); } );
暴露實體方法
對父組件暴露獲取 ECharts 實體的方法,讓父組件可直接通過實體呼叫原生函式,
defineExpose({ getInstance: () => chartInstance.value, resize, draw });
順便提一下, defineExpose
是在 <script setup>
才能使用的編譯器宏,用來顯式指定需要暴露給父組件的屬性,
完整代碼
太長了,貼出來沒人會細看,有需要的直接自取,親測有效,啟動專案就能看到,快去魔改吧 ?github
本文轉載于:
https://juejin.cn/post/7245183742264377401
如果對您有所幫助,歡迎您點個關注,我會定時更新技術檔案,大家一起討論學習,一起進步,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/555614.html
標籤:其他
下一篇:返回列表