记录--Vue3 封装 ECharts 通用组件

这篇具有很好参考价值的文章主要介绍了记录--Vue3 封装 ECharts 通用组件。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

记录--Vue3 封装 ECharts 通用组件

按需导入的配置文件

配置文件这里就不再赘述,内容都是一样的,主打一个随用随取,按需导入。

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 = 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 中没有碰到的:

记录--Vue3 封装 ECharts 通用组件

 出现这种问题是因为,我们使用 ref 接收了 echarts.init 的实例。这会导致 chartInstance 被代理成为响应式对象,影响了 ECharts 对内部属性的访问。Echarts 官方 FAQ 也阐述了该问题:

记录--Vue3 封装 ECharts 通用组件

 

所以,我们有两种解决方法:

  1. 使用 shallowRef 替换 ref
  2. 使用 ref + markRaw

shallowRef 和 ref() 不同之处在于,浅层 ref 的内部值将会原样存储和暴露,并且不会被深层递归地转为响应式。只有对 .value 的访问是响应式的。

而 markRaw 则会将一个对象标记为不可被转为代理。返回该对象本身。在有些值不应该是响应式的场景中,例如复杂的第三方类实例或 Vue 组件对象,这很有用。

记录--Vue3 封装 ECharts 通用组件

 我们这里使用 markRaw 对 init 进行包裹:

chartInstance.value = 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 触发的条件也比较奇葩,但也比较常见:

  1. 在父组件中,复用多个 ECharts 组件;
  2. 使用了 flex 或 grid 这种没有明确给定宽高的布局;

记录--Vue3 封装 ECharts 通用组件

此时会发现:当前窗口放大,正常触发 resize, 图表会随之放大。但是,此时再缩小窗口,虽然也会触发 resize,但是图表的大小却缩不回来了......

一开始还以为是我封装的写法有问题,直到搜到了ECharts 官方的 issues 才发现原来不止我一个遇到了😂

记录--Vue3 封装 ECharts 通用组件

我的理解是:首先,无论什么布局 echarts 取的都是 dom 的 clientWidth 和 clientHeight 作为容器宽高。其次,由于 flex、grid 这种布局可以不需要显示地指定 width、height,这就导致 echarts 在自适应的过程中无法明确地获取到容器的宽高,所以即便触发了 resize 事件,但是重绘的图表还是之前默认的宽高。

解决方案

给每个 flex-itemgrid-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 {
  // 略...
  onMouseover?: (...args: any[]) => any;
  onMouseout?: (...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 onMouseover = (chart: EChartsType, option: ECOption) => {
  (option.yAxis as YAXisOption).nameTextStyle.color = "#ccc";
  // 重绘图表
  chart.setOption(option);
};

// 鼠标移出,隐藏y轴 name
const onMouseout = (chart: EChartsType, option: ECOption) => {
  (option.yAxis as YAXisOption).nameTextStyle.color = "transparent";
  chart.setOption(option);
};

<template>
    <Chart
        width="100%"
        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

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 记录--Vue3 封装 ECharts 通用组件文章来源地址https://www.toymoban.com/news/detail-490576.html

到了这里,关于记录--Vue3 封装 ECharts 通用组件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 整体认识和路由配置、基础数据渲染、热榜区域实现、图片预览组件封装、认识SKU组件、通用组件统一注册全局(详情页)【Vue3】

    整体业务认识 路由配置 准备组件模板 配置路由 绑定模板测试跳转 封装接口 获取数据渲染模版 思考:渲染模版时遇到对象的多层属性访问可能出现什么问题? 模块实现整体分析 结论:两块热榜相比, 结构一致,标题title和列表内容不同 渲染基础热榜数据 1- 准备模版 2- 封

    2024年02月15日
    浏览(49)
  • Vue组件封装:基于Vue3+wangeditor富文本组件二次封装

    1.简介         开源 Web 富文本编辑器,开箱即用,配置简单。一个产品的价值,就在于解决用户的问题,提高效率、降低成本、增加稳定性和扩展性。wangEditor 不是为了做而做,也不是单纯的模仿谁,而是经过上述问题分析之后,给出一个系统的解决方案。旨在真正去解决用

    2024年04月08日
    浏览(46)
  • 记录vue3+echarts搭建数据可视化页面!

    提示:记录一下写页面的时候遇到过的一些小问题! 页面布局就是简单的用了个三栏式布局,在写页面的过程中对于多个页面共同使用的部分要注意善用组件复用,避免写冗余重复的代码! 比如说对于需要重复使用的图表容器,可以将其注册为全局组件V3Echarts.vue,当需要表

    2024年02月16日
    浏览(56)
  • vue3 echart组件封装

    项目中用到了很多echart图表,进行了简单的组件封装,主要包含以下功能: 创建图表实例,渲染图表 支持传入自定义函数,可拿到图表实例,实现个性化功能 支持配置更新后图表自动刷新,可配置是清空后再刷新 loading状态控制 resize时图表更新 支持饼图默认高亮功能 echa

    2024年03月09日
    浏览(60)
  • vue3范围选择组件封装

    个人项目地址: SubTopH前端开发个人站 (自己开发的前端功能和UI组件,一些有趣的小功能,感兴趣的伙伴可以访问,欢迎提出更好的想法,私信沟通,网站属于静态页面) SubTopH前端开发个人站 https://subtop.gitee.io/subtoph.github.io/#/home 实现效果 直接上代码 组件文件  组件使用

    2024年02月11日
    浏览(35)
  • vue3组件封装系列-表单请求

    我们在开发一些后台管理系统时,总是会写很多的列表查询页面,如果不封装组件,就会无限的复制粘贴,而且页面很冗余,正常情况下,我们都是要把组件进行二次封装,来达到我们想要效果。这里我分享一下我近期封装的关于列表的组件封装。 vue3+element-plus srccomponents

    2024年04月23日
    浏览(34)
  • vue3组件二次封装Ui处理

    在Vue开发中,我们常常需要使用UI框架提供的组件。但是UI框架的组件可能并不符合我们的需求,这时候就需要进行二次封装。下面是一些关于Vue组件二次封装Ui处理的技巧: 子组件代码: 父组件使用: 如果使用props接收弊端: 基本上组件不会只有一两个属性,属性多的话接

    2023年04月14日
    浏览(83)
  • vue3自定义封装组件:消息提示、轮播图、加载更多、骨架屏组件

    定义组件:src/components/library/xtx-infinite-loading.vue 注册组件:src/components/library/index.js  引用组件:src/main.js 使用组件: .vue文件 首先是轮播图的样式:src/components/library/xtx-carousel.vue  然后是轮播图的结构与逻辑封装:src/components/library/xtx-carousel.vue 插件注册:src/components/library

    2024年02月12日
    浏览(46)
  • 【TSX】vue3 + element-ui + tsx 通用表格组件

    简介: 基于 vue3 + el-table 封装的通用表格组件 的 tsx写法,想要参考模板写法的可以到我另一篇博客喔~ TS + vue3.2 + vite2 + element-plus 通用表格组件封装 话不多说,本组件分为四部分: 组件调用:   属性及方法使用说明: 注意:如果你在使用 Sortable插件想要拖动排序表格时,t

    2024年02月15日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包