背景:新项目采用
Vue3
作为前端项目框架,避免不了要使用echarts
,但是在使用的时候,出现了与Vue2
使用不一样的地方,所以特别记下来,希望给到有需要的同学一些帮助。
下载Echarts依赖
# 自己使用的yarn
yarn add echarts
# or
npm install echarts --save
# or
# 有淘宝镜像的可以选择 (安装速度快)
cnpm install echarts --save
创建按需引入的配置文件 echarts.ts(文件名称自定义)及进行配置
- 在你自己需要的目录下创建引入
eachrts
配置的文件,我是在src/utils
目录下创建的echarts.ts
文件(根据你自己的需求) - 在echarts.ts文件中引入echarts相关配置,网上有很多教程,但这里还是再啰嗦写一下,做戏做全套,一条龙服务。(看到最后有用给个点赞+收藏)
// 引入 echarts 核心模块,核心模块提供了 echarts 使用必须要的接口。
import * as echarts from "echarts/core";
/** 引入柱状图 + 折线图 + 饼图,图表后缀都为 Chart,一般常用的就这三个,如果还需要其他的,就自行添加 */
import { BarChart, LineChart, PieChart } from "echarts/charts";
// 引入提示框,标题,直角坐标系,数据集,内置数据转换器组件,组件后缀都为 Component
import {
TitleComponent,
TooltipComponent,
GridComponent,
DatasetComponent,
TransformComponent,
ToolboxComponent,
LegendComponent,
} from "echarts/components";
// 标签自动布局,全局过渡动画等特性
import { LabelLayout, UniversalTransition } from "echarts/features";
// 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步
import { CanvasRenderer } from "echarts/renderers";
// 注册必须的组件
echarts.use([
TitleComponent,
TooltipComponent,
GridComponent,
DatasetComponent,
TransformComponent,
ToolboxComponent,
LegendComponent,
LabelLayout,
UniversalTransition,
CanvasRenderer,
BarChart,
LineChart,
PieChart,
]);
// 导出
export default echarts;
根目录 main.ts 文件引入创建的配置文件 echarts.ts
项目另外采用了 Pinia + ElementPlus + ElementPlus(图标)
,引入方式都在下面,希望能够得到有需要的朋友帮助。
最重要的是要看本次引入echarts相关配置部分,注释也写好了,大家可以参考一下。
import { createApp } from "vue";
import { createPinia } from "pinia";
// 引入Element-plus
import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
import zhCn from "element-plus/dist/locale/zh-cn.mjs";
// 引入图标
import * as ElementPlusIconsVue from "@element-plus/icons-vue";
// 引入路由
import router from "./routes/index";
// 引入echarts
import echarts from "./utils/echarts";
// 引入整个项目入口文件
import App from "./App.vue";
// 定义全局样式
import "./style.less";
// 创建 Store 实例
const piniaStore = createPinia();
// 创建Vue实例
const app = createApp(App);
// 注册 Element-plus图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component);
}
/***********************echart 挂载 START*******************************/
// echarts 挂载到 Vue实例中
// 注意:app.config.globalProperties 和 app.provide('$echarts', echarts) 二选一即可
// Vue.prototype.$echarts = echarts; // vue2的挂载方式
app.config.globalProperties.$echarts = echarts; // vue3的挂载方式(一个用于注册能够被应用内所有组件实例访问到的全局属性的对象。)
app.provide('$echarts', echarts); // vue3采用provide, inject方式使用
/***********************echart 挂载 END*******************************/
// 挂载element-plus,使用国际化,设置图标尺寸,及弹框层级
app.use(router).use(piniaStore).use(ElementPlus, {
// size: 'small',
zIndex: 3000,
locale: zhCn,
});
// vue挂载到根节点
app.mount("#app");
组件内使用echarts
前面基本都还简单,但是到了使用的时候,很多人就不知道怎么处理了,以前在 Vue2
的时候还可以通过vue实例
拿到 this
去使用,但是到了 Vue3
里面,没有this
了,怎么办?
不着急,办法还是有的,并且还不止一种
,请接着往下看。
- 通过
getCurrentInstance()
获取组件实例,类似 Vue2 中的 this
<script setup lang="ts">
import { ref, getCurrentInstance } from 'vue';
// 获取echart挂载的DOM节点
const container = ref();
// 获取当前组件实例
const { proxy }: any = getCurrentInstance();
// echarts初始化
let myChart = proxy.$echarts.init(container.value);
const option = {
// 自定义echarts图标相关配置
};
myChart.setOption(option);
// 根据页面大小自动响应图表大小
window.addEventListener("resize", function () {
myChart.resize();
});
</script>
<template>
<div id="echarts1" ref="container"></div>
</template>
- 通过采用
provide
,inject
方式使用
<script setup lang="ts">
import { ref, inject} from 'vue';
// 获取echart挂载的DOM节点
const container = ref();
// 通过 inject 接收Echarts
const Echarts = inject('$echarts');
// echarts初始化
const myChart = (Echarts as any).init(container.value);
const option = {
// 自定义echarts图标相关配置
};
myChart.setOption(option);
// 根据页面大小自动响应图表大小
window.addEventListener("resize", function () {
myChart.resize();
});
</script>
<template>
<div id="echarts1" ref="container"></div>
</template>
上面的两种方式唯一不同的就是如何获取(接收)注册echarts。
到这里就结束了吗?
问题解决
太天真了!
这里我就要问下,上面两种方式的代码能够跑起来吗?
事实上,不行,因为上面 script
内的代码运行的时候,还没有挂载到组件实例上,找不到定义的 ref
对象 container
,即使通过document.getElementById('echarts1')
的方式获取DOM节点 <div id="echarts1" ref="container"></div>
一样报 null
或者 undefined
。
子任就会出现如下报错:Uncaught (in promise) Error: Initialize failed: invalid dom.
不信? 那么就控制台打印输出看看两种方式获取的DOM到底是个啥。
<script setup lang="ts">
import { ref } from 'vue';
// 获取echart挂载的DOM节点
const container = ref();
console.log('获取的DOM-div:', document.getElementById('echarts1'), container.value);
</script>
<template>
<div id="echarts1" ref="container"></div>
</template>
上面的代码输出一下结果:
那么解决办法也很简单,直接开启一个延时器 setTimeout
就好了。
最后完整代码如下:
<script setup lang="ts">
import { ref, inject, onBeforeUnmount} from 'vue';
// 获取echart挂载的DOM节点
const container = ref();
// 定义延时器指针对象,便于组件实例销毁的时候以便清除
const timer = ref();
// 通过 inject 接收Echarts
const Echarts = inject('$echarts');
// 或
// 通过Vue全局注册方式获取
// const {proxy}: any = getCurrentInstance();
const initEchartsOneFn = () => {
// echarts初始化
const myChart = (Echarts as any).init(container.value);
// 或
// let myChart = proxy.$echarts.init(container.value);
const option = {
// 自定义echarts图标相关配置
};
myChart.setOption(option);
// 根据页面大小自动响应图表大小
window.addEventListener("resize", function () {
myChart.resize();
});
}
// 判断定时器是否存在
if (timer.value) {
clearTimeout(timer.value);
}
// 绑定定时器,销毁的时候再次清除
timer.value = setTimeout(() => initEchartsOneFn(), 1000);
// 组件实例销毁前清除延时器对象
onBeforeUnmount(() => {
if (timer.value) clearTimeout(timer.value);
});
</script>
// 此处部分将就着看看
<template>
<div id="echarts1" ref="container"></div>
</template>
以证清白,上效果图:
最后
如果这篇文章对你有所帮助,请咚咚大家的 发财黄金手指
,点赞,收藏。
如果文章有描述错误或者有更好解决方案,也请大家多多 评论。