vue3封装简易的vue-echarts问题
作者:TwoKe
这篇文章主要介绍了vue3封装简易的vue-echarts问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
vue3封装简易的vue-echarts
项目场景
数据可视化开发,采用的技术栈是vue3+echarts+router。
问题描述
在vue2中,才开始开发数据可视化大屏,都是用echarts,之后改用为vue-echarts组件,但是到了vue3之后,组件会有一些小问题,所以准备自己封装一套简易的vue-echarts组件,其他的功能之后再迭代上去,足够项目使用即可。
代码封装
<template> <div class="echarts" :id="id"></div> </template>
这里的echarts组件的id应该每个组件不同,因此id值为动态设置的。
<script> import { onMounted } from 'vue' import { uuid } from '../../utils/index' import Echarts from 'echarts' export default { name: 'TwokeVueEcharts', props: { options: { type: Object, default: () => ({}) } }, setup (ctx) { const id = `vue-echarts-${uuid()}` let chart = null const initEcharts = () => { if (!chart) { const dom = document.getElementById(id) chart = Echarts.init(dom) }else { return } if(!ctx.options) return chart.setOption(ctx.options) } onMounted( () => { initEcharts() }) return { id } } } </script>
这里可以看到我引入了uuid的工具类,是为了生成唯一的id值,这里也可用时间戳搭配前缀来实现。
返回id值以供视图渲染。
引入的echarts为echarts官方组件
<style lang="scss" scoped> .echarts { width: 100%; height: 100%; } </style>
这里是让echarts组件可以根据外面的容器大小,铺满展示。
组件使用
两种方式:
全局安装
main.js中
import TwokeVueEcharts from './TwokeVueEcharts.vue' import { createApp } from 'vue' import App from './App.vue' createApp(App).component("vue-echarts",TwokeVueEcharts).mount("#app")
组件中引入
<script> import TwokeVueEcharts from './TwokeVueEcharts.vue' export default { components: { TwokeVueEcharts } } </script>
<template> <div style="width:300px;height:300px"> <twoke-vue-echarts :options="options"></twoke-vue-echarts> </div> </template> <script> import TwokeVueEcharts from './TwokeVueEcharts.vue' export default { components: { TwokeVueEcharts }, setup () { return { options: { tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' }, legend: { orient: 'vertical', left: 10, data: ['事件一 名称', '事件二 名称', '事件三 名称', '事件四 名称'], padding: [250, 6, 7, 8] }, grid: { top: 0, left: 0, right: 0, bottom: 0 }, series: [ { name: '访问来源', type: 'pie', radius: ['50%', '70%'], avoidLabelOverlap: false, label: { show: false, position: 'center' }, emphasis: { label: { show: true, fontSize: '30', fontWeight: 'bold' } }, labelLine: { show: false }, data: [ { value: 335, name: '事件一 名称' }, { value: 310, name: '事件二 名称' }, { value: 234, name: '事件三 名称' }, { value: 135, name: '事件四 名称' } ] } ] } } } } </script>
vue3 echarts多图自适应封装
npm install echarts -S
封装echarts插件
src/components/echarts.vue
<template> <div :id="echartsDomId"></div> </template> <script> import * as echarts from 'echarts'; import {EleResize} from "@/assets/js/esresize"; import {watch, onMounted, ref,onUnmounted} from 'vue'; export default { name: "rxp-echarts", props: { option: { type: Object, required: true, } }, setup(props) { //存储echarts的实例 var instance = null; // 监听props是否改变 watch(props, (newValue, oldValue) => { instance.dispose(); //先销毁 init(); //再创建好了 //重绘不生效啊 // instance.setOption(newValue); },{deep:true}) //计算属性 自动分配echarts的ID 。防止一个页面的echarts出现相同id const echartsDomId = 'echarts' + Math.random() * 100000; function init() { instance = echarts.init(document.getElementById(echartsDomId)); instance.setOption(props.option); //设置option //添加监听事件,如果页面宽度和高度变化, 重新绘制echarts EleResize.on(document.getElementById(echartsDomId), () => { instance.resize(); }) } //组件一旦挂载,运行echarts初始化 onMounted(() =>init()); //组件卸载,销毁echarts实例 onUnmounted(()=>instance.dispose()); /** * 父节点通过ref拿到此组件的实例,调用downloadPic函数,触发图片下载。 */ const downloadPic = (name)=>{ let content = instance.getDataURL(); // 这个方法是经过封装之后的,id就是我们最终经过init(),setOptions的echart图表,它具有getDataURL()方法 let aLink = document.createElement('a'); let blob = base64ToBlob(content); let evt = document.createEvent("HTMLEvents"); evt.initEvent("click", true, true); aLink.download = `${name}.png`; aLink.href = URL.createObjectURL(blob); aLink.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window })); } function base64ToBlob(code) { let parts = code.split(';base64,'); let contentType = parts[0].split(':')[1]; let raw = window.atob(parts[1]); let rawLength = raw.length; let uInt8Array = new Uint8Array(rawLength); for (let i = 0; i < rawLength; ++i) { uInt8Array[i] = raw.charCodeAt(i); } return new Blob([uInt8Array], { type: contentType }); } return { echartsDomId, //父节点通过ref拿到此组件的实例,调用downloadPic函数,触发图片下载。 downloadPic } } } </script> <style scoped> div{ width: 100%; height: 100%; } </style>
再封装监听窗口变化触发方法
src/assets/js/esresize.js
//EleResize('domId',回调函数) //echarts resize var EleResize = { _handleResize: function (e) { var ele = e.target || e.srcElement var trigger = ele.__resizeTrigger__ if (trigger) { var handlers = trigger.__z_resizeListeners if (handlers) { var size = handlers.length for (var i = 0; i < size; i++) { var h = handlers[i] var handler = h.handler var context = h.context handler.apply(context, [e]) } } } }, _removeHandler: function (ele, handler, context) { var handlers = ele.__z_resizeListeners if (handlers) { var size = handlers.length for (var i = 0; i < size; i++) { var h = handlers[i] if (h.handler === handler && h.context === context) { handlers.splice(i, 1) return } } } }, _createResizeTrigger: function (ele) { var obj = document.createElement('object') obj.setAttribute('style', 'display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden;opacity: 0; pointer-events: none; z-index: -1;') obj.onload = EleResize._handleObjectLoad obj.type = 'text/html' ele.appendChild(obj) obj.data = 'about:blank' return obj }, _handleObjectLoad: function () { this.contentDocument.defaultView.__resizeTrigger__ = this.__resizeElement__ this.contentDocument.defaultView.addEventListener('resize', EleResize._handleResize) } } if (document.attachEvent) { // ie9-10 EleResize.on = function (ele, handler, context) { var handlers = ele.__z_resizeListeners if (!handlers) { handlers = [] ele.__z_resizeListeners = handlers ele.__resizeTrigger__ = ele ele.attachEvent('onresize', EleResize._handleResize) } handlers.push({ handler: handler, context: context }) } EleResize.off = function (ele, handler, context) { var handlers = ele.__z_resizeListeners if (handlers) { EleResize._removeHandler(ele, handler, context) if (handlers.length === 0) { ele.detachEvent('onresize', EleResize._handleResize) delete ele.__z_resizeListeners } } } } else { EleResize.on = function (ele, handler, context) { var handlers = ele.__z_resizeListeners if (!handlers) { handlers = [] ele.__z_resizeListeners = handlers if (getComputedStyle(ele, null).position === 'static') { ele.style.position = 'relative' } var obj = EleResize._createResizeTrigger(ele) ele.__resizeTrigger__ = obj obj.__resizeElement__ = ele } handlers.push({ handler: handler, context: context }) } EleResize.off = function (ele, handler, context) { var handlers = ele.__z_resizeListeners if (handlers) { EleResize._removeHandler(ele, handler, context) if (handlers.length === 0) { var trigger = ele.__resizeTrigger__ if (trigger) { trigger.contentDocument.defaultView.removeEventListener('resize', EleResize._handleResize) ele.removeChild(trigger) delete ele.__resizeTrigger__ } delete ele.__z_resizeListeners } } } } export {EleResize}
使用echarts (vue2搬来的,vue3使用同理)
<template> <div style="width: 100%;height: 100%;"> <div style="width: 80%;height: 70%;background-color: #888888"> <!-- 注意:option是echarts的配置选项, 在为空时,不要初始化组件,所以添加v-if判断option的值存在 --> <echarts v-if="config && flag" :option="config"></echarts> </div> <button @click="flag=!flag">隐藏echarts</button> <button @click="update">修改option参数</button> <button @click="upData">修改option中的数组</button> </div> </template> <script> import echarts from "@/components/echarts"; export default { name: "helloWord", components:{echarts}, data() { return { config:null, //echarts中的配置参数 flag:true, //手动切换是否隐藏 } }, created() { this.reset(); }, methods:{ reset(){ setTimeout(()=>{ this.config={ title: { text: "我是初始化数据", }, xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] }, yAxis: { type: 'value' }, series: [ { data: [500, 230, 224, 218, 135, 147, 260], type: 'line' } ] } },2000) }, update(){ this.config={ title: { text: "我是修改后的数据", }, xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] }, yAxis: { type: 'value' }, series: [ { data: [ //随机数据 parseInt(Math.random()*100), parseInt(Math.random()*100), parseInt(Math.random()*100), parseInt(Math.random()*100), parseInt(Math.random()*100), parseInt(Math.random()*100), parseInt(Math.random()*100), ], type: 'line' } ] } }, upData(){ this.config.series={ data: [ //随机数据 parseInt(Math.random()*100), parseInt(Math.random()*100), parseInt(Math.random()*100), parseInt(Math.random()*100), parseInt(Math.random()*100), parseInt(Math.random()*100), parseInt(Math.random()*100), ], type: 'line' } } } } </script> <style scoped> </style> `` vue echarts多图自适应封装
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。