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

按需导入的配置文件

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

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

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

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

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

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

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

我们这里使用 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 这种没有明确给定宽高的布局;

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

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

我的理解是:首先,无论什么布局 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 通用组件的更多相关文章

  1. 【小程序】---- 封装Echarts公共组件,遍历图表实现多个饼图

    一.问题描述: 在小程序的项目中,封装公共的饼图组件,并在需要的页面引入使用.要求一个页面中有多个饼图,动态渲染不同的数据. 二.效果实现: 1. 查看——小程序使用Echarts的方式 2. 封装饼 ...

  2. react-native中使用Echarts,自己使用WebView封装Echarts经验

    1.工作中遇到的问题 我们在使用react-native肯定遇到过各种奇葩的问题,比如引入Echarts时候莫名报错,但是Echarts官网明显告诉我们可以懒加载的,这是因为基本上js大部分原生的组件 ...

  3. 饿了么基于Vue2.0的通用组件开发之路(分享会记录)

    Element:一套通用组件库的开发之路 Element 是由饿了么UED设计.饿了么大前端开发的一套基于 Vue 2.0 的桌面端组件库.今天我们要分享的就是开发 Element 的一些心得. 官网 ...

  4. Vue3 企业级优雅实战 - 组件库框架 - 5 组件库通用工具包

    该系列已更新文章: 分享一个实用的 vite + vue3 组件库脚手架工具,提升开发效率 开箱即用 yyg-cli 脚手架:快速创建 vue3 组件库和vue3 全家桶项目 Vue3 企业级优雅实战 ...

  5. 我的一个PLSQL函数 先查询再插入数据库的函数 动态SQL拼接查询条件、通用游标、记录定义(封装部分查询字段并赋值给游标)、insert select 序列、常量【我】

    先查询再插入数据库的函数 CREATE OR REPLACE FUNCTION F_REVENUE_SI(l_p_cd in Varchar2, l_c_cd in Varchar2, l_prod_ ...

  6. vue2升级vue3:Vue Demij打通vue2与vue3壁垒,构建通用组件

    如果你的vue2代码之前是使用vue-class-component 类组件模式写的.选择可以使用 https://github.com/facing-dev/vue-facing-decorator ...

  7. vue3 vite2 封装 SVG 图标组件 - 基于 vite 创建 vue3 全家桶项目续篇

    在<基于 vite 创建 vue3 全家桶>一文整合了 Element Plus,并将 Element Plus 中提供的图标进行全局注册,这样可以很方便的延续 Element UI 的风 ...

  8. Vue2.0的通用组件

    饿了么基于Vue2.0的通用组件开发之路(分享会记录)   Element:一套通用组件库的开发之路 Element 是由饿了么UED设计.饿了么大前端开发的一套基于 Vue 2.0 的桌面端组件库. ...

  9. 【干货】.NET开发通用组件发布(一) 介绍

    组件介绍 集合个人和团都开发中遇到的一些通用组件,邮件发送组件.内容采集.CSV数据文件导入工具.日志记录组件.MVC验证登陆组件.MVC分页组件.短信发送组件和强大的Repeate和Repeater ...

  10. 基于jQuery封装的分页组件

    前言: 由于项目需要实现分页效果,上jQuery插件库找了下,但是木有找到自己想要的效果,于是自己封装了个分页组件. 思路: 主要是初始化时基于原型建立的分页模板然后绑定动态事件并实现刷新DOM的分页 ...

随机推荐

  1. ArrayList中的遍历删除

    ArrayList 中的遍历删除 在代码编写过程中经常会遇到这样的要求:遍历一个线性表,要求只遍历一遍(时间复杂度\(O(n)\)),删除符合指定条件的元素,且要求空间复杂度 \(O(1)\). 例如 ...

  2. 架构设计理念&模型

    理念 今天我们还需要关注 DDD 吗?https://www.infoq.cn/article/should-we-focus-on-ddd 事件风暴:https://en.wikipedia.org ...

  3. jvm的简介

    什么是jvm? java虚拟机就是二进制字节码的运行环境.我们可以把jvm看做是运行在不同系统上的一个软件应用的计算机,就比如说我们要打开图片,就得用看图软件,或者我们要对文件进行解压,是不是得用解压 ...

  4. NEMU PA 2-2 实验报告

    课程地址:https://www.bilibili.com/video/BV1f7411D7P6 一.实验目的 在PA2-1中,我们实现了了解了程序的装载和对指令的解码和执行,在这一章节我们将继续深入 ...

  5. NC25045 [USACO 2007 Jan S]Balanced Lineup

    题目链接 题目 题目描述 For the daily milking, Farmer John's N cows (1 ≤ N ≤ 100,000) always line up in the sam ...

  6. XTW100编程器在Win10下的安装

    XTW100 这是一个淘宝上卖得很多的经典编程器, 用于写入24和25系列的存储芯片. 最初使用的是stm32f103c8t6, 因为f103价格飞涨, 市面上大都换成国产的兼容mcu了, 软件和使用 ...

  7. Laravel入坑指南(7)——中间件Middleware

    Laravel框架中引入了"中间件"这个概念,笔者觉得不是太合适.这里的Middleware和Java Servlet中的过滤器(Filter)就是一个东西,但是想比之下Filte ...

  8. 为什么华为今年疯狂招od?

    不知道的大家有没有发现 这两年市场不好公司用人需求紧缩 唯有华子疯狂招人 很多人都听过华为OD 但是具体是什么还是有很多人疑惑 总结以下三个部分: 1.为啥疯狂招od而不是之前的纯"外包&q ...

  9. Java JVM——4.程序计数器

    简介 JVM中的程序计数寄存器(Program Counter Register),Register的命名源于CPU的寄存器,寄存器存储指令相关的现场信息,CPU只有把数据装载到寄存器才能够运行. 这 ...

  10. 使用objc4V818.2源码编译,没有什么比苹果底层源码更有说服力去证明底层原理真假

    前言 为什么会想要调试源码? 苹果开源了部分源码, 但相似内容太多, 基本找不到代码见的对应关系, 如果能像自己工程一样进行跳转那多好哇~~ 苹果源码开源地址: https://opensource. ...