源代码
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
,这样可以确保传入的参数类型符合要求😃。