【Vue3】我用 Vue 封装了个 ECharts Hooks

张开发
2026/4/10 23:31:35 15 分钟阅读

分享文章

【Vue3】我用 Vue 封装了个 ECharts Hooks
为什么需要这个 Hooks先看看我们平时用 ECharts 的常规操作123456789101112131415161718192021222324252627282930// 常规写法letchart null;// 初始化onMounted(() {constdom document.getElementById(chart-container);if(dom) {chart echarts.init(dom);chart.setOption(option);window.addEventListener(resize, handleResize);}});// 更新数据constupdateChart (newData) {if(chart) {chart.setOption({ series: [{ data: newData }] });}};// 处理resizeconsthandleResize () {chart?.resize();};// 销毁实例onBeforeUnmount(() {window.removeEventListener(resize, handleResize);chart?.dispose();});这段代码不算复杂但每个图表都要写一遍就很折磨人了。更麻烦的是容器获取要处理各种情况DOM 元素、ID 选择器、Vue Ref频繁初始化容易导致内存泄漏事件绑定 / 解绑需要手动管理响应式数据更新要手动触发 setOptionuseEchart Hooks 来了基于以上痛点我封装了useEchartHooks核心功能包括支持多种容器类型Ref、DOM 元素、ID / 类选择器自动处理初始化与销毁响应式配置更新内置事件绑定 / 解绑方法自动监听窗口 resize废话不多说先上代码123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148//先导入Echartimport { echarts }from/Echarts;exportinterfaceRefObject {current?: HTMLElement |null;}exportinterfaceCallbackRef {(el: HTMLElement |null):void;}export type EchartsOption echarts.EChartsOption;export type container | RefHTMLElement |null| HTMLElement|string|string[];/*** 适配多种容器选择方式的 ECharts 封装* param container - 容器选择器支持 Ref, DOM 元素, ID 选择器, 类选择器* param option - 初始配置* returns {chart: echarts.ECharts | null, update: (newOption: EChartsOption) void}*/export function useEchart(container: container,option: EchartsOption {}): {chart: echarts.ECharts |null;onChartEvent: (event:string, handler: (params: any) void) void;offChartEvent: (event:string, handler: (params: any) void) void;update: (newOption: EchartsOption) void;handleResize: () void;} {letchart: echarts.ECharts |nullnull;letcontainerElement: HTMLElement |nullnull;letresizeObserver: ResizeObserver |nullnull;//ResizeObserver 实例// 辅助函数处理单个选择器constgetContainerElementForSingle (selector:string): HTMLElement |null {if(selector.startsWith(#)) {returndocument.getElementById(selector.slice(1)) ||null;}elseif(selector.startsWith(.)) {return(document.querySelector(selector)asHTMLElement) ||null;}// 直接ID 无#returndocument.getElementById(selector) ||null;};//获取容器元素constgetContainerElement (): HTMLElement |null {if(container instanceof HTMLElement) {returncontainer;}elseif(typeofcontainer string) {returngetContainerElementForSingle(container);}elseif(valueincontainer) {// Ref 类型returncontainer.value;}elseif(Array.isArray(container)) {// 多个选择器返回第一个匹配for(constselector of container) {constelement getContainerElementForSingle(selector);if(element) {returnelement;}}}returnnull;};// 初始化图表constinitChart ():void {containerElement getContainerElement();if(!containerElement) {console.error(无法获取容器元素);return;}if(!chart) {chart echarts.init(containerElement,infographic);resizeObserver newResizeObserver(() {chart?.resize();});resizeObserver.observe(containerElement);}if(option) {chart.setOption(option);}};// 处理窗口大小变化consthandleResize () {chart?.resize();};// 更新图表配置constupdate (newOption: EchartsOption):void {if(chart) {chart.setOption(newOption);}};// 新增事件绑定方法constonChartEvent (event:string, handler: (params: any) void) {chart?.on(event, handler);};constoffChartEvent (event:string, handler: (params: any) void) {chart?.off(event, handler);};// 响应式更新图表配置watch(() option,(newOption) update(newOption),{deep:true,});onMounted(() {initChart();});onBeforeUnmount(() {if(chart) {// 清理 ResizeObserver 实例if(resizeObserver) {resizeObserver.disconnect();resizeObserver null;}chart.dispose();chart null;}});return{getchart() {returnchart;},onChartEvent,offChartEvent,update,handleResize};}核心代码解析先看整体结构这个 Hooks 主要包含这些部分1234567891011121314151617181920export function useEchart(container, option) {letchart null;letcontainerElement null;// 容器获取逻辑constgetContainerElement () { ... };// 初始化图表constinitChart () { ... };// 响应式更新watch(() option, (newOption) { ... });// 生命周期管理onMounted(() initChart());onBeforeUnmount(() { ... });// 暴露APIreturn{ chart, update, onChartEvent, offChartEvent, handleResize };}1. 万能容器处理最实用的功能之一就是支持多种容器形式12345678910111213141516171819// 支持的容器类型type container RefHTMLElement |null | HTMLElement |string|string[];// 容器获取逻辑constgetContainerElement () {if(container instanceof HTMLElement) {returncontainer;}elseif(typeofcontainer string) {returngetContainerElementForSingle(container);}elseif(valueincontainer) {// Vue Refreturncontainer.value;}elseif(Array.isArray(container)) {// 多个选择器for(constselector of container) {constelement getContainerElementForSingle(selector);if(element)returnelement;}}returnnull;};无论是直接传 DOM 元素、Vue 的 Ref 对象还是 ID 选择器带 #或不带、类选择器甚至是选择器数组自动取第一个匹配项都能轻松处理。2. 自动生命周期管理初始化逻辑会在组件挂载时执行销毁时自动清理123456789101112131415161718192021222324// 初始化图表constinitChart () {containerElement getContainerElement();if(!containerElement) {console.error(无法获取容器元素);return;}if(!chart) {chart echarts.init(containerElement,infographic);chart.resize();window.addEventListener(resize, handleResize);}chart.setOption(option);};// 组件卸载时清理onBeforeUnmount(() {if(chart) {window.removeEventListener(resize, handleResize);chart.dispose();chart null;}});再也不用担心忘记解绑事件或销毁实例导致的内存泄漏了3. 响应式与事件处理内置 watch 监听配置变化自动更新图表123456789101112131415// 响应式更新图表配置watch(() option,(newOption) update(newOption),{ deep:true});// 事件绑定方法constonChartEvent (event:string, handler: (params: any) void) {chart?.on(event, handler);};constoffChartEvent (event:string, handler: (params: any) void) {chart?.off(event, handler);};如何使用用起来超级简单三步到位1. 基础使用123456789101112131415161718192021templatedivrefchartRefclasschart-container/div/templatescript setupimport {ref}fromvue;import { useEchart }from./useEchart;// 图表容器constchartRef ref(null);// 初始配置constoption ref({xAxis: { type:category, data: [Mon,Tue,Wed] },yAxis: { type:value},series: [{ data: [120, 200, 150], type:line}]});// 初始化图表const{ chart, update } useEchart(chartRef, option.value);/script2. 事件绑定123456// 绑定点击事件const{ onChartEvent } useEchart(chartRef, option.value);onChartEvent(click, (params) {console.log(点击了图表,params);});3. 动态更新数据123456789// 直接更新配置const{ update } useEchart(chartRef, option.value);// 按钮点击更新数据consthandleUpdate () {update({series: [{ data: [300, 150, 280], type:line}]});};为什么这个 Hooks 值得复用减少重复代码将通用逻辑抽象每个图表只需关注配置和业务逻辑边界处理完善包含容器不存在、重复初始化等异常情况处理灵活性高支持多种容器形式适应不同场景内存安全自动清理事件和实例避免内存泄漏响应式友好完美配合 Vue 的响应式系统数据变化自动更新图表

更多文章