這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
按需匯入的組態檔
組態檔這里就不再贅述,內容都是一樣的,主打一個隨用隨取,按需匯入,
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
標籤:其他
下一篇:返回列表

