源代碼
import type { EChartsOption } from 'echarts';import type { Ref } from 'vue';import type { Nullable } from '@vben/types';import type EchartsUI from './echarts-ui.vue';import { computed, nextTick, watch } from 'vue';import { usePreferences } from '@vben/preferences';import {tryOnUnmounted,useDebounceFn,useResizeObserver,useTimeoutFn,useWindowSize,
} from '@vueuse/core';import echarts from './echarts';type EchartsUIType = typeof EchartsUI | undefined;type EchartsThemeType = 'dark' | 'light' | null;function useEcharts(chartRef: Ref<EchartsUIType>) {let chartInstance: echarts.ECharts | null = null;let cacheOptions: EChartsOption = {};const { isDark } = usePreferences();const { height, width } = useWindowSize();const resizeHandler: () => void = useDebounceFn(resize, 200);const getOptions = computed((): EChartsOption => {if (!isDark.value) {return {};}return {backgroundColor: 'transparent',};});const initCharts = (t?: EchartsThemeType) => {const el = chartRef?.value?.$el;if (!el) {return;}chartInstance = echarts.init(el, t || isDark.value ? 'dark' : null);return chartInstance;};const renderEcharts = (options: EChartsOption,clear = true,): Promise<Nullable<echarts.ECharts>> => {cacheOptions = options;const currentOptions = {...options,...getOptions.value,};return new Promise((resolve) => {if (chartRef.value?.offsetHeight === 0) {useTimeoutFn(async () => {resolve(await renderEcharts(currentOptions));}, 30);return;}nextTick(() => {useTimeoutFn(() => {if (!chartInstance) {const instance = initCharts();if (!instance) return;}clear && chartInstance?.clear();chartInstance?.setOption(currentOptions);resolve(chartInstance);}, 30);});});};function resize() {chartInstance?.resize({animation: {duration: 300,easing: 'quadraticIn',},});}watch([width, height], () => {resizeHandler?.();});useResizeObserver(chartRef as never, resizeHandler);watch(isDark, () => {if (chartInstance) {chartInstance.dispose();initCharts();renderEcharts(cacheOptions);resize();}});tryOnUnmounted(() => {// 銷毀實例,釋放資源chartInstance?.dispose();});return {renderEcharts,resize,getChartInstance: () => chartInstance,};
}export { useEcharts };export type { EchartsUIType };
這段 TypeScript 代碼定義了一個名為 useEcharts
的自定義鉤子函數,用于在 Vue 項目中集成和管理 ECharts 圖表。下面是對代碼詳細的解釋:
導入模塊
import type { EChartsOption } from 'echarts'; // 導入 ECharts 選項類型
import type { Ref } from 'vue'; // 導入 Vue 的 Ref 類型
import type { Nullable } from '@vben/types'; // 導入可空類型
import type EchartsUI from './echarts-ui.vue'; // 導入 EchartsUI 組件類型import { computed, nextTick, watch } from 'vue'; // 導入 Vue 的計算屬性、下一個 DOM 更新周期方法和監聽器
import { usePreferences } from '@vben/preferences'; // 導入偏好設置鉤子
import {tryOnUnmounted,useDebounceFn,useResizeObserver,useTimeoutFn,useWindowSize,
} from '@vueuse/core'; // 導入 VueUse 核心庫的一些鉤子
import echarts from './echarts'; // 導入 ECharts 庫
- 類型導入:從不同的庫中導入了所需的類型,包括 ECharts 選項類型、Vue 的
Ref
類型、可空類型以及EchartsUI
組件類型。 - 功能導入:從
vue
中導入了computed
、nextTick
和watch
等功能,從@vben/preferences
中導入了偏好設置鉤子,從@vueuse/core
中導入了一些實用的鉤子,最后導入了 ECharts 庫。
類型定義
type EchartsUIType = typeof EchartsUI | undefined; // 定義 EchartsUI 組件類型
type EchartsThemeType = 'dark' | 'light' | null; // 定義 ECharts 主題類型
EchartsUIType
:表示EchartsUI
組件類型,可能為undefined
。EchartsThemeType
:表示 ECharts 的主題類型,有'dark'
、'light'
或null
三種可能。
useEcharts
函數
function useEcharts(chartRef: Ref<EchartsUIType>) {let chartInstance: echarts.ECharts | null = null; // 初始化 ECharts 實例let cacheOptions: EChartsOption = {}; // 初始化緩存的 ECharts 選項const { isDark } = usePreferences(); // 獲取是否為深色模式const { height, width } = useWindowSize(); // 獲取窗口的高度和寬度const resizeHandler: () => void = useDebounceFn(resize, 200); // 創建防抖的 resize 處理函數const getOptions = computed((): EChartsOption => {if (!isDark.value) {return {};}return {backgroundColor: 'transparent',};}); // 計算根據深色模式的 ECharts 選項
- 變量初始化:
chartInstance
:用于存儲 ECharts 實例,初始值為null
。cacheOptions
:用于緩存 ECharts 選項,初始值為空對象。
- 狀態獲取:
isDark
:通過usePreferences
鉤子獲取當前是否為深色模式。height
和width
:通過useWindowSize
鉤子獲取窗口的高度和寬度。
- 防抖處理:使用
useDebounceFn
創建一個防抖的resize
處理函數,防抖時間為 200 毫秒。 - 計算屬性:
getOptions
是一個計算屬性,根據isDark
的值返回不同的 ECharts 選項。如果是深色模式,返回一個包含透明背景色的選項;否則返回空對象。
初始化 ECharts 實例
const initCharts = (t?: EchartsThemeType) => {const el = chartRef?.value?.$el;if (!el) {return;}chartInstance = echarts.init(el, t || isDark.value ? 'dark' : null);return chartInstance;}; // 初始化 ECharts 實例
initCharts
函數用于初始化 ECharts 實例。它首先獲取chartRef
對應的 DOM 元素,如果元素不存在則直接返回。然后使用echarts.init
方法初始化 ECharts 實例,并根據傳入的主題或當前的深色模式設置主題。最后返回初始化后的實例。
渲染 ECharts 圖表
const renderEcharts = (options: EChartsOption,clear = true,): Promise<Nullable<echarts.ECharts>> => {cacheOptions = options;const currentOptions = {...options,...getOptions.value,};return new Promise((resolve) => {if (chartRef.value?.offsetHeight === 0) {useTimeoutFn(async () => {resolve(await renderEcharts(currentOptions));}, 30);return;}nextTick(() => {useTimeoutFn(() => {if (!chartInstance) {const instance = initCharts();if (!instance) return;}clear && chartInstance?.clear();chartInstance?.setOption(currentOptions);resolve(chartInstance);}, 30);});});}; // 渲染 ECharts 圖表
renderEcharts
函數用于渲染 ECharts 圖表。它接收兩個參數:options
表示要渲染的 ECharts 選項,clear
表示是否清除之前的圖表內容,默認為true
。- 函數首先將傳入的
options
緩存到cacheOptions
中,然后合并options
和getOptions
的值得到currentOptions
。 - 如果
chartRef
的高度為 0,說明圖表容器還未完全渲染,使用useTimeoutFn
延遲 30 毫秒后再次調用renderEcharts
函數。 - 否則,在
nextTick
中使用useTimeoutFn
延遲 30 毫秒后進行圖表的渲染。如果chartInstance
不存在,則調用initCharts
函數初始化實例。然后根據clear
的值決定是否清除之前的圖表內容,并使用setOption
方法設置新的選項。最后通過resolve
返回chartInstance
。
調整 ECharts 圖表大小
function resize() {chartInstance?.resize({animation: {duration: 300,easing: 'quadraticIn',},});} // 調整 ECharts 圖表大小
resize
函數用于調整 ECharts 圖表的大小。它調用chartInstance
的resize
方法,并設置了一個動畫效果,動畫持續時間為 300 毫秒,緩動函數為'quadraticIn'
。
監聽事件
watch([width, height], () => {resizeHandler?.();}); // 監聽窗口大小變化,調用 resize 處理函數useResizeObserver(chartRef as never, resizeHandler); // 監聽圖表容器大小變化,調用 resize 處理函數watch(isDark, () => {if (chartInstance) {chartInstance.dispose();initCharts();renderEcharts(cacheOptions);resize();}}); // 監聽深色模式變化,重新初始化和渲染圖表
- 窗口大小監聽:使用
watch
監聽width
和height
的變化,當窗口大小改變時,調用防抖的resizeHandler
函數。 - 容器大小監聽:使用
useResizeObserver
監聽chartRef
對應的 DOM 元素的大小變化,當容器大小改變時,調用resizeHandler
函數。 - 深色模式監聽:使用
watch
監聽isDark
的變化,當深色模式改變時,銷毀當前的chartInstance
,重新初始化 ECharts 實例,渲染緩存的選項,并調整圖表大小。
組件卸載處理
tryOnUnmounted(() => {// 銷毀實例,釋放資源chartInstance?.dispose();}); // 在組件卸載時銷毀 ECharts 實例
- 使用
tryOnUnmounted
鉤子,在組件卸載時銷毀chartInstance
,釋放資源。
導出
return {renderEcharts,resize,getChartInstance: () => chartInstance,};
}export { useEcharts }; // 導出 useEcharts 鉤子
export type { EchartsUIType }; // 導出 EchartsUIType 類型
useEcharts
函數返回一個對象,包含renderEcharts
、resize
和getChartInstance
三個方法。- 最后導出
useEcharts
鉤子和EchartsUIType
類型,供其他組件使用。
🎉 綜上所述,這段代碼通過自定義鉤子 useEcharts
封裝了 ECharts 的初始化、渲染、大小調整和主題切換等功能,方便在 Vue 項目中使用 ECharts 圖表。同時,通過監聽窗口和容器大小變化以及深色模式的改變,實現了圖表的自適應和動態更新。
第一章 代碼整體概述
1.1 代碼功能簡介
1.1.1 代碼核心功能
此代碼定義了一個名為 useEcharts
的自定義鉤子,這個鉤子就像是一個神奇的小助手🧙?♂?,專門用于在 Vue 項目中管理 ECharts 圖表的各種操作:
- 初始化:就像為一場演出搭建舞臺一樣,它會為 ECharts 圖表準備好初始的環境和設置,讓圖表能夠順利“登場”。
- 渲染:把數據轉化為直觀的圖表,就如同畫家把顏料變成美麗的畫作🎨,讓用戶能夠清晰地看到數據的展示效果。
- 調整大小:當頁面大小發生變化時,它能像一個靈活的舞者一樣,自動調整圖表的大小,保證圖表在不同的屏幕尺寸下都能完美呈現。
- 深色模式適配:隨著用戶在淺色和深色模式之間切換,它能像一個智能的調光師一樣,讓圖表的顏色和樣式也隨之改變,提供更好的視覺體驗🌙。
1.1.2 代碼使用場景
這個代碼適用于需要在 Vue 組件中集成 ECharts 圖表,并且有以下需求的場景:
- 響應式調整圖表大小:比如在不同尺寸的設備上瀏覽網頁,或者用戶手動調整瀏覽器窗口大小時,圖表能夠自適應調整大小,始終保持良好的顯示效果📱💻。
- 支持深色模式切換:現在很多應用都提供了深色模式,當用戶切換到深色模式時,圖表也能相應地調整顏色和樣式,避免在深色背景下看不清圖表內容🌃。
1.2 代碼結構概述
1.2.1 導入模塊
代碼中導入了各種模塊,這些模塊就像是不同的工具,共同協作完成代碼的功能:
- ECharts 相關類型:這些類型就像是說明書,告訴代碼如何正確地使用 ECharts 的各種功能和數據結構📖。
- Vue 相關模塊:為代碼提供了 Vue 框架的支持,讓代碼能夠與 Vue 組件完美結合,實現數據的響應式更新和組件的生命周期管理🖥?。
- 自定義偏好設置鉤子:可以根據用戶的個性化設置,對圖表進行相應的調整,比如用戶可能有自己喜歡的圖表顏色、字體等🎨。
- VueUse 核心庫的鉤子:VueUse 是一個非常實用的工具庫,其鉤子可以幫助代碼更方便地實現一些常見的功能,比如監聽窗口大小變化等🕵??♂?。
1.2.2 類型定義
定義了 EchartsUIType
和 EchartsThemeType
類型,它們的作用和用途如下:
EchartsUIType
:就像是一個分類標簽🏷?,用于對 ECharts 圖表的 UI 樣式進行分類和定義,讓代碼能夠更清晰地管理和使用不同的 UI 樣式。EchartsThemeType
:類似于一個主題模板🎨,用于定義 ECharts 圖表的主題,比如淺色主題、深色主題等,方便在不同的主題之間進行切換。
1.2.3 鉤子函數主體
useEcharts
函數的整體結構和主要邏輯流程就像是一場精心策劃的演出🎭:
- 初始化階段:準備好各種必要的參數和設置,就像演員們在后臺化妝、換裝,為演出做好準備。
- 渲染階段:根據傳入的數據和設置,將圖表渲染到頁面上,就像演員們在舞臺上精彩表演,展示出最終的效果。
- 監聽階段:持續監聽頁面大小變化和深色模式切換等事件,一旦有變化就及時調整圖表,就像舞臺工作人員時刻關注著舞臺的情況,及時調整燈光、道具等。
1.2.4 導出內容
導出的 useEcharts
鉤子和 EchartsUIType
類型有著重要的作用:
useEcharts
鉤子:就像是一個可以復用的工具包🛠?,其他 Vue 組件可以通過導入這個鉤子,輕松地在自己的組件中集成 ECharts 圖表,并使用其提供的各種功能。EchartsUIType
類型:為其他代碼提供了一個統一的 UI 樣式分類標準,方便不同的組件在使用 ECharts 圖表時,能夠遵循相同的 UI 規范,保持整體的一致性👔。
第二章 導入模塊詳解
2.1 ECharts 相關導入
2.1.1 EChartsOption
類型導入
1. 作用解釋
EChartsOption
類型就像是一份嚴格的“設計藍圖”🎨,它是 ECharts 配置選項的類型定義。在我們使用 ECharts 繪制圖表時,需要傳入一個配置對象來告訴 ECharts 我們想要繪制什么樣的圖表,比如圖表的類型(柱狀圖、折線圖等)、數據內容、樣式設置等。而 EChartsOption
類型的存在,就是為了規范這個傳入的配置對象。
它可以幫助我們在編寫代碼時,提前發現配置對象中可能存在的錯誤,比如屬性名拼寫錯誤、屬性類型不匹配等。就好比在建造房子之前,先有一份詳細準確的藍圖,這樣在施工過程中就能避免很多不必要的錯誤和麻煩。
例如,在 TypeScript 中使用 ECharts 時,如果我們定義一個配置對象并指定其類型為 EChartsOption
:
import { EChartsOption } from 'echarts';const option: EChartsOption = {xAxis: {type: 'category',data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']},yAxis: {type: 'value'},series: [{data: [820, 932, 901, 934, 1290, 1330, 1320],type: 'line'}]
};
這樣,當我們寫錯屬性名或者屬性類型不匹配時,編譯器就會及時給我們報錯提示。
2.1.2 echarts
庫導入
1. 導入目的
導入 echarts
庫就像是打開了一個功能強大的“工具箱”🧰,我們可以使用這個工具箱里的各種工具來完成 ECharts 的初始化、渲染等功能。
ECharts 庫提供了一系列的方法和屬性,讓我們能夠方便地創建和操作圖表。比如,我們可以使用 echarts.init
方法來初始化一個 ECharts 實例,然后使用 setOption
方法將配置對象應用到這個實例上,從而實現圖表的渲染。
示例代碼如下:
import * as echarts from 'echarts';// 初始化 ECharts 實例
const myChart = echarts.init(document.getElementById('main'));// 設置配置項并渲染圖表
const option = {// 配置項內容
};
myChart.setOption(option);
通過導入 echarts
庫,我們就可以利用這些功能輕松地在網頁上繪制出各種精美的圖表。
2.2 Vue 相關導入
2.2.2.1 Ref
類型導入
1. 作用解釋
在 Vue 中,Ref
類型就像是一個“魔法盒子”🪄,用于創建響應式引用。它可以讓我們在代碼中方便地引用某個值,并且當這個值發生變化時,與之關聯的 DOM 元素或者其他依賴項會自動更新。
在我們的代碼中,Ref
類型主要用于引用 EchartsUI 組件。通過 Ref
,我們可以獲取到 EchartsUI 組件的實例,進而對其進行操作。
例如:
<template><div><EchartsUI ref="echartsRef" /></div>
</template><script lang="ts" setup>
import { ref } from 'vue';
import EchartsUI from './EchartsUI.vue';// 創建一個 Ref 引用
const echartsRef = ref<InstanceType<typeof EchartsUI>>();// 可以在需要的時候使用 echartsRef 來訪問 EchartsUI 組件的實例
</script>
這樣,我們就可以通過 echartsRef.value
來訪問 EchartsUI 組件的實例,調用其方法或者獲取其屬性。
2.2.2 computed
、nextTick
和 watch
導入
1. computed
的作用
computed
就像是一個“智能計算器”🧮,用于創建計算屬性。計算屬性是根據其他數據動態計算得出的屬性,它會根據依賴的數據自動更新。
當我們有一些數據需要根據其他數據進行計算時,使用計算屬性可以讓代碼更加簡潔和易于維護。例如:
<template><div><p>原始數據: {{ num }}</p><p>計算結果: {{ doubleNum }}</p></div>
</template><script lang="ts" setup>
import { ref, computed } from 'vue';const num = ref(10);// 創建計算屬性
const doubleNum = computed(() => num.value * 2);
</script>
在這個例子中,doubleNum
就是一個計算屬性,它的值會根據 num
的變化而自動更新。
2. nextTick
的作用
nextTick
就像是一個“時間旅行者”🕵??♂?,用于在 DOM 更新后執行回調。在 Vue 中,數據的更新是異步的,當我們修改數據后,DOM 不會立即更新。而 nextTick
可以讓我們在 DOM 更新完成后再執行一些操作。
例如,當我們需要獲取更新后的 DOM 元素的尺寸或者位置時,就可以使用 nextTick
:
<template><div><button @click="updateData">更新數據</button><p>{{ message }}</p></div>
</template><script lang="ts" setup>
import { ref, nextTick } from 'vue';const message = ref('原始消息');const updateData = async () => {// 修改數據message.value = '更新后的消息';// 在 DOM 更新后執行回調await nextTick();// 這里可以獲取更新后的 DOM 元素console.log('DOM 已更新');
};
</script>
這樣,我們就可以確保在 DOM 更新完成后再執行相應的操作。
3. watch
的作用
watch
就像是一個“監控攝像頭”📹,用于監聽數據變化并執行相應操作。當我們需要在某個數據發生變化時執行一些特定的邏輯時,就可以使用 watch
。
例如:
<template><div><input v-model="inputValue" /></div>
</template><script lang="ts" setup>
import { ref, watch } from 'vue';const inputValue = ref('');// 監聽 inputValue 的變化
watch(inputValue, (newValue, oldValue) => {console.log(`輸入的值從 ${oldValue} 變為 ${newValue}`);
});
</script>
在這個例子中,當 inputValue
的值發生變化時,watch
會自動觸發回調函數,我們可以在回調函數中執行相應的操作。
2.3 自定義模塊導入
2.3.1 usePreferences
導入
1. 作用解釋
usePreferences
鉤子就像是一個“個人秘書”📋,用于獲取用戶的偏好設置。在我們的代碼中,主要用于判斷用戶是否選擇了深色模式。
用戶的偏好設置可能包括主題模式、字體大小、語言等,通過 usePreferences
鉤子,我們可以方便地獲取這些設置,并根據用戶的偏好來調整頁面的顯示效果。
例如:
<template><div :class="{ 'dark-mode': isDarkMode }"><!-- 頁面內容 --></div>
</template><script lang="ts" setup>
import { usePreferences } from './usePreferences';// 獲取用戶偏好設置
const { isDarkMode } = usePreferences();
</script>
這樣,我們就可以根據 isDarkMode
的值來動態添加或移除 dark-mode
類,從而實現深色模式的切換。
2.3.2 EchartsUI
組件導入
1. 導入目的
導入 EchartsUI
組件就像是為圖表找到了一個“家”🏠,其目的是為了獲取圖表容器的 DOM 元素。
EchartsUI 組件通常是一個包含圖表容器的組件,我們通過導入這個組件并在頁面中使用它,就可以獲取到這個容器的 DOM 元素,然后在這個容器中初始化和渲染 ECharts 圖表。
例如:
<template><div><EchartsUI ref="chartContainer" /></div>
</template><script lang="ts" setup>
import { ref } from 'vue';
import EchartsUI from './EchartsUI.vue';// 創建一個 Ref 引用
const chartContainer = ref<HTMLElement>();// 在需要的時候可以使用 chartContainer.value 來訪問圖表容器的 DOM 元素
</script>
這樣,我們就可以通過 chartContainer.value
來獲取圖表容器的 DOM 元素,進而在這個元素上初始化 ECharts 實例。
2.4 VueUse 核心庫導入
2.4.1 tryOnUnmounted
導入
1. 作用解釋
tryOnUnmounted
就像是一個“清潔工”🧹,用于在組件卸載時執行清理操作。在我們的代碼中,主要用于銷毀 ECharts 實例。
當一個組件被卸載時,如果不及時清理一些資源,可能會導致內存泄漏等問題。而 tryOnUnmounted
可以確保在組件卸載時,我們能夠執行一些必要的清理操作,比如銷毀 ECharts 實例。
例如:
<template><div><EchartsUI ref="echartsRef" /></div>
</template><script lang="ts" setup>
import { ref } from 'vue';
import { tryOnUnmounted } from '@vueuse/core';
import EchartsUI from './EchartsUI.vue';const echartsRef = ref<InstanceType<typeof EchartsUI>>();// 在組件卸載時銷毀 ECharts 實例
tryOnUnmounted(() => {if (echartsRef.value) {// 銷毀 ECharts 實例的邏輯}
});
</script>
這樣,當組件被卸載時,就會自動執行銷毀 ECharts 實例的操作,避免資源的浪費。
2.4.2 useDebounceFn
導入
1. 用途說明
useDebounceFn
就像是一個“節流閥”🚥,通過防抖處理減少 resize
函數的調用頻率,提高性能。
當我們監聽元素的大小變化時,比如窗口大小變化或者圖表容器大小變化,resize
事件可能會頻繁觸發。如果每次觸發都執行 resize
函數,會導致性能問題。而 useDebounceFn
可以讓 resize
函數在一段時間內只執行一次,避免不必要的性能開銷。
例如:
<template><div><EchartsUI ref="echartsRef" /></div>
</template><script lang="ts" setup>
import { ref } from 'vue';
import { useDebounceFn } from '@vueuse/core';
import EchartsUI from './EchartsUI.vue';const echartsRef = ref<InstanceType<typeof EchartsUI>>();// 定義 resize 函數
const handleResize = () => {if (echartsRef.value) {// 執行圖表的 resize 操作}
};// 使用防抖處理
const debouncedResize = useDebounceFn(handleResize, 300);// 在需要監聽大小變化的地方調用 debouncedResize
</script>
在這個例子中,useDebounceFn
會讓 handleResize
函數在 300 毫秒內只執行一次,從而減少了函數的調用頻率,提高了性能。
2.4.3 useResizeObserver
導入
1. 作用解釋
useResizeObserver
就像是一個“尺寸偵探”🕵?,用于監聽元素大小變化。在我們的代碼中,主要用于監聽圖表容器的大小變化并調用 resize
函數。
當圖表容器的大小發生變化時,我們需要及時調整圖表的大小,以保證圖表能夠正確顯示。useResizeObserver
可以幫助我們實時監測圖表容器的大小變化,一旦發生變化,就會觸發相應的回調函數,我們可以在回調函數中調用 resize
函數來調整圖表的大小。
例如:
<template><div><EchartsUI ref="echartsRef" /></div>
</template><script lang="ts" setup>
import { ref } from 'vue';
import { useResizeObserver } from '@vueuse/core';
import EchartsUI from './EchartsUI.vue';const echartsRef = ref<InstanceType<typeof EchartsUI>>();// 監聽圖表容器的大小變化
useResizeObserver(echartsRef, () => {if (echartsRef.value) {// 調用 resize 函數調整圖表大小}
});
</script>
這樣,當圖表容器的大小發生變化時,就會自動調用 resize
函數來調整圖表的大小,保證圖表的顯示效果。
2.4.4 useTimeoutFn
導入
1. 用途說明
useTimeoutFn
就像是一個“定時鬧鐘”?,用于在指定時間后執行回調函數。在代碼中用于處理圖表渲染時的延遲操作。
有時候,我們需要在圖表渲染之前或者之后執行一些延遲操作,比如等待數據加載完成后再渲染圖表,或者在圖表渲染完成后執行一些動畫效果。useTimeoutFn
可以幫助我們實現這些延遲操作。
例如:
<template><div><EchartsUI ref="echartsRef" /></div>
</template><script lang="ts" setup>
import { ref } from 'vue';
import { useTimeoutFn } from '@vueuse/core';
import EchartsUI from './EchartsUI.vue';const echartsRef = ref<InstanceType<typeof EchartsUI>>();// 在 1000 毫秒后執行回調函數
const { start } = useTimeoutFn(() => {if (echartsRef.value) {// 執行圖表渲染的邏輯}
}, 1000);// 可以在需要的時候調用 start 方法開始計時
start();
</script>
這樣,就可以在 1000 毫秒后執行圖表渲染的邏輯,實現延遲操作。
2.4.5 useWindowSize
導入
1. 作用解釋
useWindowSize
就像是一個“窗口測量員”📏,用于獲取窗口的高度和寬度,以便在窗口大小變化時調整圖表大小。
當窗口大小發生變化時,圖表的大小也需要相應地調整,以保證圖表能夠適應不同的窗口尺寸。useWindowSize
可以實時獲取窗口的高度和寬度,我們可以根據這些信息來動態調整圖表的大小。
例如:
<template><div><EchartsUI ref="echartsRef" /></div>
</template><script lang="ts" setup>
import { ref } from 'vue';
import { useWindowSize } from '@vueuse/core';
import EchartsUI from './EchartsUI.vue';const echartsRef = ref<InstanceType<typeof EchartsUI>>();// 獲取窗口的高度和寬度
const { width, height } = useWindowSize();// 監聽窗口大小變化,調整圖表大小
watch([width, height], () => {if (echartsRef.value) {// 根據窗口大小調整圖表大小的邏輯}
});
</script>
這樣,當窗口大小發生變化時,就可以根據新的窗口高度和寬度來調整圖表的大小,保證圖表的顯示效果。
第三章 類型定義詳解
3.1 EchartsUIType
類型
3.1.1 類型定義
EchartsUIType
被定義為 typeof EchartsUI | undefined
,這到底是什么意思呢🤔?
typeof EchartsUI
:這里的typeof
是一個操作符,它會返回EchartsUI
的類型。也就是說,它代表的是EchartsUI
這個組件本身所具有的類型。想象一下,EchartsUI
就像是一個獨特的“物品”,而typeof EchartsUI
就是描述這個“物品”特征的標簽🏷?。| undefined
:這個|
符號在類型定義里表示“或”的關系。undefined
是 JavaScript 中的一個原始值,表示變量已聲明但未賦值,或者函數沒有返回值。所以| undefined
意味著EchartsUIType
除了可以是EchartsUI
的類型,還可以是未定義的情況。就好比一個盒子,里面要么裝著EchartsUI
這個“物品”,要么就是空的(未定義)🎁。
綜上所述,EchartsUIType
表示的就是 EchartsUI
組件的類型或者未定義的情況。
3.1.2 用途
這個類型在代碼中主要用于 chartRef
的類型定義,這是為什么呢😉?
在 React 等框架中,ref
是一種用來引用 DOM 節點或者組件實例的方式。chartRef
就是用來引用 EchartsUI
組件的一個引用對象。通過將 chartRef
的類型定義為 EchartsUIType
,可以確保我們引用的是 EchartsUI
組件。
舉個例子,如果我們不小心把 chartRef
引用到了其他類型的組件或者變量上,由于類型不匹配,編譯器就會發出警告??,這樣就能避免一些潛在的錯誤。就好像我們給一個特定的“停車位”(chartRef
)設置了只能停放“EchartsUI
汽車”的規則,一旦有其他“車輛”試圖停進去,就會被阻止🚗🚫。
3.2 EchartsThemeType
類型
3.2.1 類型定義
EchartsThemeType
被定義為 'dark' | 'light' | null
,下面來詳細解釋一下🧐。
'dark'
和'light'
:ECharts 是一個強大的可視化庫,它支持不同的主題,其中'dark'
代表深色主題,'light'
代表淺色主題。這就好比我們可以給一幅畫選擇不同的背景顏色,深色背景會讓畫面顯得神秘、沉穩,淺色背景則會讓畫面更加明亮、清新🌈。null
:在 JavaScript 中,null
表示一個空對象指針。在這里,null
表示不指定任何主題。就好像我們選擇不給畫設置背景顏色,讓它保持默認的樣子🖼?。
所以,EchartsThemeType
表示的就是 ECharts 支持的主題類型。
3.2.2 用途
這個類型在代碼中的主要使用場景是在 initCharts
函數中指定 ECharts 實例的主題。
initCharts
函數的作用是初始化一個 ECharts 實例,在初始化的過程中,我們可以通過傳入 EchartsThemeType
類型的參數來指定使用的主題。例如:
function initCharts(theme: EchartsThemeType) {// 初始化 ECharts 實例const myChart = echarts.init(dom, theme);// 其他初始化操作// ...
}
通過這種方式,我們可以根據不同的需求靈活地選擇 ECharts 實例的主題。如果我們想要一個深色主題的圖表,就可以調用 initCharts('dark')
;如果想要淺色主題,就調用 initCharts('light')
;如果不想指定主題,就調用 initCharts(null)
。這樣可以讓我們的圖表在不同的場景下都能呈現出最佳的視覺效果🌟。
第四章 useEcharts
鉤子函數詳解
4.1 變量初始化
4.1.1 chartInstance
變量
chartInstance
變量在 useEcharts
鉤子函數中扮演著至關重要的角色😃。它的主要作用是存儲 ECharts 實例。想象一下,ECharts 實例就像是一個功能強大的“魔法盒子”,里面包含了圖表的各種屬性和方法。通過將這個“魔法盒子”存儲在 chartInstance
變量中,我們就可以在后續的代碼里輕松地對圖表進行各種操作,比如渲染圖表,讓它在頁面上顯示出來🎨;或者調整圖表的大小,使它能夠完美適配不同的屏幕尺寸📏。
4.1.2 cacheOptions
變量
cacheOptions
變量就像是一個“倉庫”📦,它的用途是緩存傳入的 ECharts 配置選項。在實際開發中,我們可能會根據不同的條件或者用戶的操作來重新渲染圖表。這時候,就可以從 cacheOptions
這個“倉庫”里取出之前存儲的配置選項,然后用這些選項來重新渲染圖表,避免了重復傳遞配置選項的麻煩,提高了代碼的效率和可維護性。
4.2 響應式數據獲取
4.2.1 isDark
獲取
通過 usePreferences
鉤子來獲取 isDark
變量,這就像是給我們的代碼裝上了一個“模式探測器”🕵?。isDark
變量用于判斷當前是否處于深色模式。在不同的模式下,圖表的顯示效果可能會有所不同,比如在深色模式下,我們可能需要調整圖表的背景顏色、字體顏色等配置,讓圖表在深色背景下也能清晰地顯示出來。所以,獲取 isDark
變量可以幫助我們根據當前的模式動態地調整圖表的配置。
4.2.2 width
和 height
獲取
使用 useWindowSize
鉤子來獲取窗口的寬度和高度,就像是給我們的圖表安裝了一個“尺寸追蹤器”📐。在現代的網頁設計中,頁面需要能夠自適應不同的屏幕尺寸。當用戶調整瀏覽器窗口的大小時,圖表也需要相應地調整大小,以保證良好的用戶體驗。通過獲取窗口的寬度和高度,我們可以監聽窗口大小的變化,并在變化發生時調用相應的函數來調整圖表的大小,讓圖表始終與窗口大小保持一致。
4.3 防抖處理
4.3.1 resizeHandler
函數
resizeHandler
函數是通過 useDebounceFn
對 resize
函數進行防抖處理后的函數。想象一下,當用戶頻繁地調整窗口大小時,如果每次窗口大小發生微小的變化都立即調用 resize
函數來調整圖表大小,那么會導致 resize
函數被頻繁調用,這不僅會消耗大量的性能,還可能會讓圖表的調整效果變得不流暢。而 resizeHandler
函數就像是一個“緩沖器”🧽,它會在用戶停止調整窗口大小一段時間后才調用 resize
函數,減少了 resize
函數的調用頻率,提高了性能,讓圖表的調整更加平滑。
4.4 計算屬性 getOptions
4.4.1 計算邏輯
getOptions
計算屬性的計算邏輯就像是一個“智能轉換器”🔄。它會根據 isDark
的值來返回不同的 ECharts 配置選項。當 isDark
為 true
,也就是處于深色模式時,它會將圖表的背景顏色設置為透明,這樣可以讓圖表更好地融入深色背景中。而當 isDark
為 false
時,它會返回默認的配置選項。
4.4.2 用途
這個計算屬性在代碼中的使用場景就像是一個“拼圖塊”🧩。它主要用于合并到最終的 ECharts 配置選項中。在渲染圖表時,我們需要將各種配置選項組合在一起,而 getOptions
計算屬性提供了根據不同模式動態生成的配置選項,將它合并到最終的配置中,就可以讓圖表根據當前的模式進行正確的渲染。
4.5 initCharts
函數
4.5.1 函數功能
initCharts
函數的主要功能是初始化 ECharts 實例,就像是給圖表“搭建一個家”🏠。它會根據傳入的主題類型或者當前的深色模式來設置圖表的主題。不同的主題可以讓圖表呈現出不同的風格,比如明亮的風格或者深色的風格,以滿足不同用戶的需求。
4.5.2 實現細節
在函數內部,首先需要獲取圖表容器的 DOM 元素,這就像是找到圖表“家”的具體位置📍。通常會使用 document.getElementById
或者 ref
等方式來獲取 DOM 元素。然后,調用 echarts.init
方法,傳入圖表容器的 DOM 元素和主題類型,就可以初始化一個 ECharts 實例,將這個實例存儲在 chartInstance
變量中,方便后續的操作。
4.6 renderEcharts
函數
4.6.1 函數功能
renderEcharts
函數的主要功能是渲染 ECharts 圖表,就像是給圖表“穿上漂亮的衣服”👗。它會處理一些特殊情況,比如圖表容器高度為 0 的情況,這時候可能需要等待容器高度正常后再進行渲染。同時,它會在 DOM 更新后進行渲染操作,確保圖表能夠正確地顯示在頁面上。
4.6.2 實現細節
在函數內部,首先會合并配置選項,將 cacheOptions
和 getOptions
等配置選項組合在一起,形成最終的配置。然后,會處理延遲渲染的情況,比如使用 nextTick
等方法確保在 DOM 更新后再進行渲染。接著,會清除之前的圖表,避免出現重疊或者顯示異常的問題。最后,調用 chartInstance.setOption
方法,將最終的配置選項設置到 ECharts 實例中,完成圖表的渲染。
4.6.3 返回值
函數返回的 Promise
對象就像是一個“承諾使者”📜。它在圖表渲染完成后返回 ECharts 實例。我們可以通過 then
方法來處理這個返回的實例,比如在圖表渲染完成后進行一些額外的操作,如添加事件監聽器等。
4.7 resize
函數
4.7.1 函數功能
resize
函數的作用是調整 ECharts 實例的大小,就像是給圖表“量身定制衣服”🧵。它還設置了動畫效果,讓圖表在調整大小時能夠平滑地過渡,給用戶帶來更好的視覺體驗。
4.7.2 實現細節
在函數內部,會調用 chartInstance.resize
方法,傳入一些配置參數,如動畫效果的配置等,來實現圖表大小的調整。通過這個方法,ECharts 實例會根據新的大小重新計算和繪制圖表,讓圖表適應新的尺寸。
4.8 監聽器
4.8.1 窗口大小變化監聽
watch([width, height], () => { resizeHandler?.(); })
就像是一個“窗口變化哨兵”👮。當窗口的寬度或者高度發生變化時,它會觸發回調函數,調用 resizeHandler
函數進行圖表大小的調整。這樣,無論用戶如何調整窗口大小,圖表都能及時地做出響應,保持良好的顯示效果。
4.8.2 圖表容器大小變化監聽
useResizeObserver(chartRef as never, resizeHandler)
就像是一個“容器變化偵探”🕵??♂?。它會監聽圖表容器的大小變化,當容器的大小發生改變時,會調用 resizeHandler
函數進行圖表大小的調整。這對于一些動態改變容器大小的場景非常有用,比如在頁面布局發生變化時,圖表能夠自動調整大小。
4.8.3 深色模式變化監聽
watch(isDark, () => { ... })
就像是一個“模式變化警報器”🚨。當深色模式發生變化時,它會觸發回調函數。在回調函數中,會銷毀當前的 ECharts 實例,就像是拆除舊的“房子”,然后重新初始化并渲染圖表,給圖表換上適應新模式的“衣服”,讓圖表在不同的模式下都能正常顯示。
4.9 組件卸載處理
4.9.1 tryOnUnmounted
函數
tryOnUnmounted
函數的作用是在組件卸載時銷毀 ECharts 實例,就像是在離開“房子”時關閉所有的電器設備,釋放資源?。如果不銷毀 ECharts 實例,它會一直占用內存,可能會導致內存泄漏,影響頁面的性能。通過在組件卸載時銷毀實例,可以避免這種問題,讓頁面更加穩定和高效。
4.10 返回值
4.10.1 返回對象的屬性
返回對象中包含了 renderEcharts
、resize
和 getChartInstance
方法。
renderEcharts
方法就像是一個“渲染大師”🎨,用于渲染圖表,將配置選項應用到圖表上,讓圖表顯示在頁面上。resize
方法就像是一個“尺寸調整師”📏,用于調整圖表的大小,讓圖表適應不同的屏幕尺寸。getChartInstance
方法就像是一個“實例獲取員”👨?🔬,用于獲取 ECharts 實例,方便在其他地方對圖表進行進一步的操作。通過返回這些方法,外部組件可以方便地使用useEcharts
鉤子提供的功能,實現圖表的渲染、調整等操作。
第五章 導出內容詳解
5.1 useEcharts
鉤子導出
5.1.1 導出目的
在開發 Vue 項目時,我們經常會使用 ECharts 庫來創建各種精美的圖表。為了更方便地在不同的 Vue 組件中管理和使用 ECharts 圖表,我們將 useEcharts
鉤子導出。這個鉤子封裝了與 ECharts 相關的一些通用邏輯,比如圖表的初始化、數據更新、事件綁定等。通過導出這個鉤子,其他 Vue 組件可以直接引入并使用它,避免了在每個組件中重復編寫相同的 ECharts 管理代碼,提高了代碼的復用性和可維護性😎。
5.1.2 使用方式
以下是在其他組件中使用 useEcharts
鉤子的詳細步驟:
- 引入鉤子
在需要使用useEcharts
鉤子的 Vue 組件中,首先要引入它。假設useEcharts
鉤子定義在hooks/useEcharts.js
文件中,引入代碼如下:
import { useEcharts } from '@/hooks/useEcharts';
- 在組件中調用
在組件的setup
函數中調用useEcharts
鉤子,并根據需要進行配置。示例代碼如下:
<template><div ref="chartRef" style="width: 600px; height: 400px;"></div>
</template><script setup>
import { ref } from 'vue';
import { useEcharts } from '@/hooks/useEcharts';// 創建一個 ref 用于引用圖表容器
const chartRef = ref(null);// 調用 useEcharts 鉤子
const { initChart } = useEcharts(chartRef);// 初始化圖表
initChart({xAxis: {type: 'category',data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']},yAxis: {type: 'value'},series: [{data: [120, 200, 150, 80, 70, 110, 130],type: 'bar'}]
});
</script>
在上述代碼中,我們首先創建了一個 chartRef
用于引用圖表的容器元素。然后調用 useEcharts
鉤子,并傳入 chartRef
。最后,調用 initChart
方法并傳入 ECharts 的配置項來初始化圖表🎉。
5.2 EchartsUIType
類型導出
5.2.2 使用方式
5.2.1 導出目的
在 TypeScript 項目中,類型定義非常重要,它可以幫助我們在開發過程中更早地發現類型錯誤,提高代碼的健壯性。EchartsUIType
類型是對 ECharts 相關 UI 元素的類型定義,比如圖表的配置項、樣式等。通過導出 EchartsUIType
類型,其他模塊可以直接引用這個類型,確保在使用 ECharts 相關數據時類型的一致性和準確性🧐。
5.2.2 使用方式
以下是在其他模塊中使用 EchartsUIType
類型的詳細步驟:
- 引入類型
在需要使用EchartsUIType
類型的模塊中,首先要引入它。假設EchartsUIType
類型定義在types/EchartsUIType.ts
文件中,引入代碼如下:
import { EchartsUIType } from '@/types/EchartsUIType';
- 在類型注解中引用該類型
在代碼中需要使用 ECharts 相關數據的地方,可以使用EchartsUIType
類型進行類型注解。示例代碼如下:
import { EchartsUIType } from '@/types/EchartsUIType';function updateChartConfig(config: EchartsUIType): void {// 在這里可以對圖表配置進行更新操作console.log('Updating chart config:', config);
}const chartConfig: EchartsUIType = {xAxis: {type: 'category',data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']},yAxis: {type: 'value'},series: [{data: [120, 200, 150, 80, 70, 110, 130],type: 'bar'}]
};updateChartConfig(chartConfig);
在上述代碼中,我們定義了一個 updateChartConfig
函數,它接受一個 EchartsUIType
類型的參數。然后創建了一個 chartConfig
對象,并將其類型注解為 EchartsUIType
。最后調用 updateChartConfig
函數并傳入 chartConfig
,這樣可以確保傳入的參數類型符合要求😃。